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
diff --git a/sky/framework/animation/generator.dart b/sky/framework/animation/generator.dart index 65c373f..065d1e5 100644 --- a/sky/framework/animation/generator.dart +++ b/sky/framework/animation/generator.dart
@@ -61,14 +61,15 @@ 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 @@ { 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;
diff --git a/sky/framework/components/ink_splash.dart b/sky/framework/components/ink_splash.dart index a4c0a25..b31cc5f 100644 --- a/sky/framework/components/ink_splash.dart +++ b/sky/framework/components/ink_splash.dart
@@ -27,8 +27,11 @@ : _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;
diff --git a/sky/framework/components/scrollable.dart b/sky/framework/components/scrollable.dart index 20f2858..838dbe9 100644 --- a/sky/framework/components/scrollable.dart +++ b/sky/framework/components/scrollable.dart
@@ -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 @@ 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 @@ void didUnmount() { super.didUnmount(); _stopFling(); + _stopScrollAnimation(); } bool scrollBy(double scrollDelta) { @@ -37,6 +44,25 @@ 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 @@ _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) {