OLD | NEW |
(Empty) | |
| 1 part of widgets; |
| 2 |
| 3 abstract class FixedHeightScrollable extends Component { |
| 4 |
| 5 static Style _style = new Style(''' |
| 6 overflow: hidden; |
| 7 position: relative; |
| 8 will-change: transform;''' |
| 9 ); |
| 10 |
| 11 static Style _scrollAreaStyle = new Style(''' |
| 12 position:relative; |
| 13 will-change: transform;''' |
| 14 ); |
| 15 |
| 16 double itemHeight; |
| 17 double height; |
| 18 double minOffset; |
| 19 double maxOffset; |
| 20 |
| 21 double _scrollOffset = 0.0; |
| 22 FlingCurve _flingCurve; |
| 23 int _flingAnimationId; |
| 24 |
| 25 FixedHeightScrollable({ |
| 26 Object key, |
| 27 this.itemHeight, |
| 28 this.height, |
| 29 this.minOffset, |
| 30 this.maxOffset |
| 31 }) : super(key: key) {} |
| 32 |
| 33 |
| 34 List<Node> renderItems(int start, int count); |
| 35 |
| 36 Node render() { |
| 37 int drawCount = (height / itemHeight).round() + 1; |
| 38 double alignmentDelta = -_scrollOffset % itemHeight; |
| 39 if (alignmentDelta != 0.0) { |
| 40 alignmentDelta -= itemHeight; |
| 41 } |
| 42 |
| 43 double drawStart = _scrollOffset + alignmentDelta; |
| 44 int itemNumber = (drawStart / itemHeight).floor(); |
| 45 |
| 46 var transformStyle = |
| 47 'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)'; |
| 48 |
| 49 var items = renderItems(itemNumber, drawCount); |
| 50 |
| 51 return new Container( |
| 52 style: _style, |
| 53 onFlingStart: _handleFlingStart, |
| 54 onFlingCancel: _handleFlingCancel, |
| 55 onScrollUpdate: _handleScrollUpdate, |
| 56 onWheel: _handleWheel, |
| 57 children: [ |
| 58 new Container( |
| 59 style: _scrollAreaStyle, |
| 60 inlineStyle: transformStyle, |
| 61 children: items |
| 62 ) |
| 63 ] |
| 64 ); |
| 65 } |
| 66 |
| 67 void willUnmount() { |
| 68 _stopFling(); |
| 69 } |
| 70 |
| 71 bool _scrollBy(double scrollDelta) { |
| 72 var newScrollOffset = _scrollOffset + scrollDelta; |
| 73 if (minOffset != null && newScrollOffset < minOffset) { |
| 74 newScrollOffset = minOffset; |
| 75 } else if (maxOffset != null && newScrollOffset > maxOffset) { |
| 76 newScrollOffset = maxOffset; |
| 77 } |
| 78 if (newScrollOffset == _scrollOffset) { |
| 79 return false; |
| 80 } |
| 81 |
| 82 setState(() { |
| 83 _scrollOffset = newScrollOffset; |
| 84 }); |
| 85 return true; |
| 86 } |
| 87 |
| 88 void _scheduleFlingUpdate() { |
| 89 _flingAnimationId = sky.window.requestAnimationFrame(_updateFling); |
| 90 } |
| 91 |
| 92 void _stopFling() { |
| 93 if (_flingAnimationId == null) { |
| 94 return; |
| 95 } |
| 96 |
| 97 sky.window.cancelAnimationFrame(_flingAnimationId); |
| 98 _flingCurve = null; |
| 99 _flingAnimationId = null; |
| 100 } |
| 101 |
| 102 void _updateFling(double timeStamp) { |
| 103 double scrollDelta = _flingCurve.update(timeStamp); |
| 104 if (!_scrollBy(scrollDelta)) |
| 105 return _stopFling(); |
| 106 _scheduleFlingUpdate(); |
| 107 } |
| 108 |
| 109 void _handleScrollUpdate(sky.Event event) { |
| 110 _scrollBy(-event.dy); |
| 111 } |
| 112 |
| 113 void _handleFlingStart(sky.Event event) { |
| 114 setState(() { |
| 115 _flingCurve = new FlingCurve(-event.velocityY, event.timeStamp); |
| 116 _scheduleFlingUpdate(); |
| 117 }); |
| 118 } |
| 119 |
| 120 void _handleFlingCancel(sky.Event event) { |
| 121 _stopFling(); |
| 122 } |
| 123 |
| 124 void _handleWheel(sky.Event event) { |
| 125 _scrollBy(-event.offsetY); |
| 126 } |
| 127 } |
OLD | NEW |