mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Stabilize initial scroll simulation velocity on Android (#8543)
Previously, we would create a simulation whose initial velocity did not match the requested parameters. We now compute the parameters for the simulation in a way that ensures all the math works out. Fixes #8255
This commit is contained in:
parent
ae1a719e03
commit
1bb164ab0d
|
@ -107,52 +107,41 @@ class BouncingScrollSimulation extends SimulationGroup {
|
|||
// simplifications have been made.
|
||||
class ClampingScrollSimulation extends Simulation {
|
||||
/// Creates a scroll physics simulation that matches Android scrolling.
|
||||
//
|
||||
// TODO(ianh): The incoming `velocity` is used to determine the starting speed
|
||||
// and duration, but does not represent the exact velocity of the simulation
|
||||
// at t=0 as it should. This causes crazy scrolling irregularities when the
|
||||
// scroll dimensions change during a fling.
|
||||
ClampingScrollSimulation({
|
||||
@required this.position,
|
||||
@required this.velocity,
|
||||
this.friction: 0.015,
|
||||
Tolerance tolerance: Tolerance.defaultTolerance,
|
||||
}) : super(tolerance: tolerance) {
|
||||
_scaledFriction = friction * _decelerationForFriction(0.84); // See mPhysicalCoeff
|
||||
assert(_flingVelocityPenetration(0.0) == _kInitialVelocityPenetration);
|
||||
_duration = _flingDuration(velocity);
|
||||
_distance = _flingDistance(velocity);
|
||||
_distance = (velocity * _duration / _kInitialVelocityPenetration).abs();
|
||||
}
|
||||
|
||||
final double position;
|
||||
final double velocity;
|
||||
final double friction;
|
||||
|
||||
double _scaledFriction;
|
||||
double _duration;
|
||||
double _distance;
|
||||
|
||||
// See DECELERATION_RATE.
|
||||
static final double _decelerationRate = math.log(0.78) / math.log(0.9);
|
||||
static final double _kDecelerationRate = math.log(0.78) / math.log(0.9);
|
||||
|
||||
// See computeDeceleration().
|
||||
double _decelerationForFriction(double friction) {
|
||||
static double _decelerationForFriction(double friction) {
|
||||
return friction * 61774.04968;
|
||||
}
|
||||
|
||||
// See getSplineDeceleration().
|
||||
double _flingDeceleration(double velocity) {
|
||||
return math.log(0.35 * velocity.abs() / _scaledFriction);
|
||||
}
|
||||
|
||||
// See getSplineFlingDuration(). Returns a value in seconds.
|
||||
double _flingDuration(double velocity) {
|
||||
return math.exp(_flingDeceleration(velocity) / (_decelerationRate - 1.0));
|
||||
}
|
||||
// See mPhysicalCoeff
|
||||
final double scaledFriction = friction * _decelerationForFriction(0.84);
|
||||
|
||||
// See getSplineFlingDistance().
|
||||
double _flingDistance(double velocity) {
|
||||
final double rate = _decelerationRate / (_decelerationRate - 1.0) * _flingDeceleration(velocity);
|
||||
return _scaledFriction * math.exp(rate);
|
||||
// See getSplineDeceleration().
|
||||
final double deceleration = math.log(0.35 * velocity.abs() / scaledFriction);
|
||||
|
||||
return math.exp(deceleration / (_kDecelerationRate - 1.0));
|
||||
}
|
||||
|
||||
// Based on a cubic curve fit to the Scroller.computeScrollOffset() values
|
||||
|
@ -170,13 +159,14 @@ class ClampingScrollSimulation extends Simulation {
|
|||
// Scale f(t) so that 0.0 <= f(t) <= 1.0
|
||||
// f(t) = (1165.03 t^3 - 3143.62 t^2 + 2945.87 t) / 961.0
|
||||
// = 1.2 t^3 - 3.27 t^2 + 3.065 t
|
||||
double _flingDistancePenetration(double t) {
|
||||
return (1.2 * t * t * t) - (3.27 * t * t) + (3.065 * t);
|
||||
static const double _kInitialVelocityPenetration = 3.065;
|
||||
static double _flingDistancePenetration(double t) {
|
||||
return (1.2 * t * t * t) - (3.27 * t * t) + (_kInitialVelocityPenetration * t);
|
||||
}
|
||||
|
||||
// The derivative of the _flingDistancePenetration() function.
|
||||
double _flingVelocityPenetration(double t) {
|
||||
return (3.63693 * t * t) - (6.5424 * t) + 3.06542;
|
||||
static double _flingVelocityPenetration(double t) {
|
||||
return (3.6 * t * t) - (6.54 * t) + _kInitialVelocityPenetration;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -188,7 +178,7 @@ class ClampingScrollSimulation extends Simulation {
|
|||
@override
|
||||
double dx(double time) {
|
||||
final double t = (time / _duration).clamp(0.0, 1.0);
|
||||
return _distance * _flingVelocityPenetration(t) * velocity.sign;
|
||||
return _distance * _flingVelocityPenetration(t) * velocity.sign / _duration;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
26
packages/flutter/test/widgets/scroll_simulation_test.dart
Normal file
26
packages/flutter/test/widgets/scroll_simulation_test.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() {
|
||||
test('ClampingScrollSimulation has a stable initial conditions', () {
|
||||
void checkInitialConditions(double position, double velocity) {
|
||||
ClampingScrollSimulation simulation = new ClampingScrollSimulation(position: position, velocity: velocity);
|
||||
expect(simulation.x(0.0), closeTo(position, 0.00001));
|
||||
expect(simulation.dx(0.0), closeTo(velocity, 0.00001));
|
||||
}
|
||||
|
||||
checkInitialConditions(51.0, 2866.91537);
|
||||
checkInitialConditions(584.0, 2617.294734);
|
||||
checkInitialConditions(345.0, 1982.785934);
|
||||
checkInitialConditions(0.0, 1831.366634);
|
||||
checkInitialConditions(-156.2, 1541.57665);
|
||||
checkInitialConditions(4.0, 1139.250439);
|
||||
checkInitialConditions(4534.0, 1073.553798);
|
||||
checkInitialConditions(75.0, 614.2093);
|
||||
checkInitialConditions(5469.0, 182.114534);
|
||||
});
|
||||
}
|
|
@ -91,6 +91,6 @@ void main() {
|
|||
expect(log, equals(<String>['tap 18']));
|
||||
await tester.tap(find.byType(Scrollable));
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(log, equals(<String>['tap 18', 'tap 43']));
|
||||
expect(log, equals(<String>['tap 18', 'tap 42']));
|
||||
}, skip: Platform.isMacOS); // Skip due to https://github.com/flutter/flutter/issues/6961
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue