Implement an OverscrollCurve for Scrollable When using OverscrollCurve, we continue to scroll beyond the top of the scrollable area but the scroll delta is reduced by 2x. A future CL will add an animation at gesturescrollend to relax back to scroll position 0.0. R=eseidel@chromium.org Review URL: https://codereview.chromium.org/1002953003
diff --git a/sky/examples/stocks-fn/stocklist.dart b/sky/examples/stocks-fn/stocklist.dart index d904ef1..883ee37 100644 --- a/sky/examples/stocks-fn/stocklist.dart +++ b/sky/examples/stocks-fn/stocklist.dart
@@ -8,7 +8,7 @@ Object key, this.stocks, this.query - }) : super(key: key, scrollCurve: new BoundedScrollCurve(minOffset: 0.0)); + }) : super(key: key, scrollCurve: new OverscrollCurve()); List<Node> buildItems(int start, int count) { return stocks
diff --git a/sky/framework/animation/scroll_curve.dart b/sky/framework/animation/scroll_curve.dart index 07940ae..54b6385 100644 --- a/sky/framework/animation/scroll_curve.dart +++ b/sky/framework/animation/scroll_curve.dart
@@ -24,3 +24,18 @@ return newScrollOffset; } } + +class OverscrollCurve extends ScrollCurve { + double apply(double scrollOffset, double scrollDelta) { + double newScrollOffset = scrollOffset + scrollDelta; + if (newScrollOffset < 0.0) { + // If we're overscrolling, we want move the scroll offset 2x slower than + // we would otherwise. Therefore, we "rewind" the newScrollOffset by half + // the amount that we moved it above. Notice that we clap the "old" value + // to 0.0 so that we only reduce the portion of scrollDelta that's applied + // beyond 0.0. + newScrollOffset -= (newScrollOffset - math.min(0.0, scrollOffset)) / 2.0; + } + return newScrollOffset; + } +}
diff --git a/sky/framework/components/fixed_height_scrollable.dart b/sky/framework/components/fixed_height_scrollable.dart index 4f3a9d9..b388b17 100644 --- a/sky/framework/components/fixed_height_scrollable.dart +++ b/sky/framework/components/fixed_height_scrollable.dart
@@ -4,6 +4,7 @@ import '../animation/scroll_curve.dart'; import '../fn.dart'; +import 'dart:math' as math; import 'dart:sky' as sky; import 'scrollable.dart'; @@ -52,17 +53,24 @@ var transformStyle = ''; if (_height > 0.0) { - drawCount = (_height / _itemHeight).round() + 1; - double alignmentDelta = -scrollOffset % _itemHeight; - if (alignmentDelta != 0.0) { - alignmentDelta -= _itemHeight; + if (scrollOffset < 0.0) { + double visibleHeight = _height + scrollOffset; + drawCount = (visibleHeight / _itemHeight).round() + 1; + transformStyle = + 'transform: translateY(${(-scrollOffset).toStringAsFixed(2)}px)'; + } else { + drawCount = (_height / _itemHeight).round() + 1; + double alignmentOffset = math.max(0.0, scrollOffset); + double alignmentDelta = -scrollOffset % _itemHeight; + if (alignmentDelta != 0.0) + alignmentDelta -= _itemHeight; + + double drawStart = scrollOffset + alignmentDelta; + itemNumber = (drawStart / _itemHeight).floor(); + + transformStyle = + 'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)'; } - - double drawStart = scrollOffset + alignmentDelta; - itemNumber = (drawStart / _itemHeight).floor(); - - transformStyle = - 'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)'; } return new Container(