Scrollable should settle back to 0.0

This CL teaches Scrollable how to settle back to a scroll offset of 0.0 after a
fling or a scroll. There's still some room for improvement:

1) Some of this logic should be factored out into the scroll curve object.
2) We don't produce the correct animation curves when we fling into the
   overscroll region because we wait for the fling velocity to reach zero
   before we start the settling animation.

R=ojan@chromium.org, eseidel@chromium.org

Review URL: https://codereview.chromium.org/1005753002
This commit is contained in:
Adam Barth 2015-03-12 14:33:37 -07:00
parent 52d2d67754
commit 70aa7795c7
3 changed files with 68 additions and 12 deletions

View File

@ -61,14 +61,15 @@ class AnimationGenerator extends FrameGenerator {
Stream<double> _stream;
bool _done = false;
AnimationGenerator(this.duration, {
AnimationGenerator({
this.initialDelay: 0.0,
this.duration,
this.begin: 0.0,
this.end: 1.0,
this.curve: linear,
Function onDone
}):super(onDone: onDone) {
assert(duration > 0);
assert(duration != null && duration > 0.0);
double startTime = 0.0;
_stream = super.onTick.map((timeStamp) {
if (startTime == 0.0)
@ -132,8 +133,12 @@ class Animation {
{ Curve curve: linear, double initialDelay: 0.0 }) {
stop();
_animation = new AnimationGenerator(duration, begin: _value, end: newValue,
curve: curve, initialDelay: initialDelay);
_animation = new AnimationGenerator(
duration: duration,
begin: _value,
end: newValue,
curve: curve,
initialDelay: initialDelay);
_animation.onTick.listen(_setValue, onDone: () {
_animation = null;

View File

@ -27,8 +27,11 @@ class SplashAnimation {
: _offsetX = x - rect.left,
_offsetY = y - rect.top {
_animation = new AnimationGenerator(_kSplashDuration,
end: _kSplashSize, curve: easeOut, onDone: onDone);
_animation = new AnimationGenerator(
duration:_kSplashDuration,
end: _kSplashSize,
curve: easeOut,
onDone: onDone);
_styleChanged = _animation.onTick.map((p) => '''
top: ${_offsetY - p/2}px;

View File

@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../animation/curves.dart';
import '../animation/fling_curve.dart';
import '../animation/generator.dart';
import '../animation/scroll_curve.dart';
import '../fn.dart';
import 'dart:sky' as sky;
@ -14,8 +16,12 @@ abstract class Scrollable extends Component {
double _scrollOffset = 0.0;
FlingCurve _flingCurve;
int _flingAnimationId;
AnimationGenerator _scrollAnimation;
Scrollable({Object key, this.scrollCurve}) : super(key: key) {
events.listen('pointerdown', _handlePointerDown);
events.listen('pointerup', _handlePointerUpOrCancel);
events.listen('pointercancel', _handlePointerUpOrCancel);
events.listen('gestureflingstart', _handleFlingStart);
events.listen('gestureflingcancel', _handleFlingCancel);
events.listen('gesturescrollupdate', _handleScrollUpdate);
@ -25,6 +31,7 @@ abstract class Scrollable extends Component {
void didUnmount() {
super.didUnmount();
_stopFling();
_stopScrollAnimation();
}
bool scrollBy(double scrollDelta) {
@ -37,6 +44,25 @@ abstract class Scrollable extends Component {
return true;
}
void animateScrollTo(double targetScrollOffset, {
double initialDelay: 0.0,
double duration: 0.0,
Curve curve: linear}) {
_stopScrollAnimation();
_scrollAnimation = new AnimationGenerator(
duration: duration,
begin: _scrollOffset,
end: targetScrollOffset,
initialDelay: initialDelay,
curve: curve);
_scrollAnimation.onTick.listen((newScrollOffset) {
if (!scrollBy(newScrollOffset - _scrollOffset))
_stopScrollAnimation();
}, onDone: () {
_scrollAnimation = null;
});
}
void _scheduleFlingUpdate() {
_flingAnimationId = sky.window.requestAnimationFrame(_updateFling);
}
@ -49,26 +75,48 @@ abstract class Scrollable extends Component {
_flingAnimationId = null;
}
void _stopScrollAnimation() {
if (_scrollAnimation == null)
return;
_scrollAnimation.cancel();
_scrollAnimation = null;
}
void _updateFling(double timeStamp) {
double scrollDelta = _flingCurve.update(timeStamp);
if (!scrollBy(scrollDelta))
return _stopFling();
return _settle();
_scheduleFlingUpdate();
}
void _settle() {
_stopFling();
if (_scrollOffset < 0.0)
animateScrollTo(0.0, duration: 200.0, curve: easeOut);
}
void _handlePointerDown(_) {
_stopFling();
_stopScrollAnimation();
}
void _handlePointerUpOrCancel(_) {
if (_flingCurve == null)
_settle();
}
void _handleScrollUpdate(sky.GestureEvent event) {
scrollBy(-event.dy);
}
void _handleFlingStart(sky.GestureEvent event) {
setState(() {
_flingCurve = new FlingCurve(-event.velocityY, event.timeStamp);
_scheduleFlingUpdate();
});
_stopScrollAnimation();
_flingCurve = new FlingCurve(-event.velocityY, event.timeStamp);
_scheduleFlingUpdate();
}
void _handleFlingCancel(sky.GestureEvent event) {
_stopFling();
_settle();
}
void _handleWheel(sky.WheelEvent event) {