-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Closed
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listf: scrollingViewports, list views, slivers, etc.Viewports, list views, slivers, etc.found in release: 3.3Found to occur in 3.3Found to occur in 3.3found in release: 3.5Found to occur in 3.5Found to occur in 3.5frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work on
Description
The problem
When using ClampingScrollSimulation, there are two problems:
- It has large nonzero velocity when near the end (see figure below). We all know, for list view scrolling to feel comfortable, it should decrease its velocity and get to near-zero value when near end, instead of a large nonzero value.
- velocity even increases slightly there (again see figure below). We know velocity should at least decrease due to friction.
Reproduction code for "velocity increases when near end"
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('ClampingScrollSimulation should have decreasing speed', () {
final ClampingScrollSimulation simulation =
ClampingScrollSimulation(position: 0, velocity: 1000);
final List<double> tValues = List<double>.generate(60, (int i) => i / 60);
final List<double> xValues = tValues.map(simulation.x).toList();
final List<double> xDeltas = <double>[
for (int i = 0; i < xValues.length - 1; ++i) xValues[i + 1] - xValues[i]
];
// If curious, uncomment this to see the real values
// debugPrint('$tValues\n$xValues\n$xDeltas');
for (int i = 0; i < xDeltas.length - 1; ++i) {
expect(xDeltas[i], greaterThan(xDeltas[i + 1]));
}
});
}Figures
Code:
Details
import matplotlib
import numpy as np
matplotlib.use("MacOSX")
import matplotlib.pyplot as plt
import math
from dataclasses import dataclass
_kDecelerationRate = math.log(0.78) / math.log(0.9)
_initialVelocityPenetration = 3.065
def _decelerationForFriction(friction: float):
return friction * 61774.04968
def _flingDistancePenetration(t):
return (1.2 * t * t * t) - (3.27 * t * t) + (_initialVelocityPenetration * t)
def _flingVelocityPenetration(t):
return (3.6 * t * t) - (6.54 * t) + _initialVelocityPenetration
def clampDouble(x, min, max):
if x < min:
return min
if x > max:
return max
return x
def _sign(x):
if x > 0:
return 1.0
if x < 0:
return -1.0
return x
def _array_map(x, f):
return np.fromiter((f(xi) for xi in x), x.dtype)
@dataclass
class ClampingScrollSimulation:
position: float
velocity: float
friction: float = 0.015
def __post_init__(self):
self._duration = self._flingDuration(self.velocity)
self._distance = abs(self.velocity * self._duration / _initialVelocityPenetration)
def _flingDuration(self, velocity: float):
scaledFriction = self.friction * _decelerationForFriction(0.84)
deceleration = math.log(0.35 * abs(velocity) / scaledFriction)
return math.exp(deceleration / (_kDecelerationRate - 1.0))
def x(self, time):
t = clampDouble(time / self._duration, 0.0, 1.0)
return self.position + self._distance * _flingDistancePenetration(t) * _sign(self.velocity)
def dx(self, time):
t = clampDouble(time / self._duration, 0.0, 1.0)
return self._distance * _flingVelocityPenetration(t) * _sign(self.velocity) / self._duration
# %%
plt.clf()
plt.tight_layout()
ax1 = plt.gca()
ax2 = ax1.twinx()
dt = 1 / 60
t = np.arange(0, 1, dt)
simulation_a = ClampingScrollSimulation(position=0, velocity=1000)
x_a = _array_map(t, simulation_a.x)
v_a = _array_map(t, simulation_a.dx)
diffx_a = (x_a[1:] - x_a[:-1]) / dt
ax1.plot(t, x_a, label='x_a')
ax2.plot(t, v_a, label='v_a')
ax2.plot(t[:-1], diffx_a, label='diffx_a')
if False:
b_start_time = 0.3
simulation_b = ClampingScrollSimulation(
position=simulation_a.x(b_start_time),
velocity=simulation_a.dx(b_start_time),
)
x_b = _array_map(t, simulation_b.x)
v_b = _array_map(t, simulation_b.dx)
diffx_b = (x_b[1:] - x_b[:-1]) / dt
ax1.plot(t + b_start_time, x_b, label='x_b')
ax2.plot(t + b_start_time, v_b, label='v_b')
ax2.plot(t[:-1] + b_start_time, diffx_b, label='diffx_b')
ax1.legend(loc="upper left")
ax2.legend(loc="upper right")
plt.xlim([0, 1])
plt.show()Image:
zoom in:
zoom in again:
gnprice
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listf: scrollingViewports, list views, slivers, etc.Viewports, list views, slivers, etc.found in release: 3.3Found to occur in 3.3Found to occur in 3.3found in release: 3.5Found to occur in 3.5Found to occur in 3.5frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work on


