Index: sky/framework/components/scrollable.dart |
diff --git a/sky/framework/components/scrollable.dart b/sky/framework/components/scrollable.dart |
index 20f285866ef2bc171fe1cec966d8b358b490f107..838dbe9c818d25c568c2632d29f3d281ddf53fe0 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 @@ 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) { |