| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 import '../animation/fling_curve.dart'; | |
| 6 import '../fn.dart'; | 5 import '../fn.dart'; |
| 7 import 'dart:sky' as sky; | 6 import 'dart:sky' as sky; |
| 7 import 'scrollable.dart'; |
| 8 | 8 |
| 9 abstract class FixedHeightScrollable extends Component { | 9 abstract class FixedHeightScrollable extends Scrollable { |
| 10 // TODO(rafaelw): This component really shouldn't have an opinion | 10 // TODO(rafaelw): This component really shouldn't have an opinion |
| 11 // about how it is sized. The owning component should decide whether | 11 // about how it is sized. The owning component should decide whether |
| 12 // it's explicitly sized or flexible or whatever... | 12 // it's explicitly sized or flexible or whatever... |
| 13 static final Style _style = new Style(''' | 13 static final Style _style = new Style(''' |
| 14 overflow: hidden; | 14 overflow: hidden; |
| 15 position: relative; | 15 position: relative; |
| 16 flex: 1; | 16 flex: 1; |
| 17 will-change: transform;''' | 17 will-change: transform;''' |
| 18 ); | 18 ); |
| 19 | 19 |
| 20 static final Style _scrollAreaStyle = new Style(''' | 20 static final Style _scrollAreaStyle = new Style(''' |
| 21 position:relative; | 21 position:relative; |
| 22 will-change: transform;''' | 22 will-change: transform;''' |
| 23 ); | 23 ); |
| 24 | 24 |
| 25 double minOffset; | |
| 26 double maxOffset; | |
| 27 | |
| 28 double _scrollOffset = 0.0; | |
| 29 FlingCurve _flingCurve; | |
| 30 int _flingAnimationId; | |
| 31 double _height = 0.0; | 25 double _height = 0.0; |
| 32 double _itemHeight; | 26 double _itemHeight; |
| 33 | 27 |
| 34 FixedHeightScrollable({ | 28 FixedHeightScrollable({ |
| 35 Object key, | 29 Object key, |
| 36 this.minOffset, | 30 double minOffset, |
| 37 this.maxOffset | 31 double maxOffset |
| 38 }) : super(key: key) { | 32 }) : super(key: key, minOffset: minOffset, maxOffset: maxOffset) { |
| 39 events.listen('gestureflingstart', _handleFlingStart); | |
| 40 events.listen('gestureflingcancel', _handleFlingCancel); | |
| 41 events.listen('gesturescrollupdate', _handleScrollUpdate); | |
| 42 events.listen('wheel', _handleWheel); | |
| 43 } | 33 } |
| 44 | 34 |
| 45 List<Node> buildItems(int start, int count); | |
| 46 | |
| 47 void didMount() { | 35 void didMount() { |
| 36 super.didMount(); |
| 48 var root = getRoot(); | 37 var root = getRoot(); |
| 49 var item = root.firstChild.firstChild; | 38 var item = root.firstChild.firstChild; |
| 50 sky.ClientRect scrollRect = root.getBoundingClientRect(); | 39 sky.ClientRect scrollRect = root.getBoundingClientRect(); |
| 51 sky.ClientRect itemRect = item.getBoundingClientRect(); | 40 sky.ClientRect itemRect = item.getBoundingClientRect(); |
| 52 assert(scrollRect.height > 0); | 41 assert(scrollRect.height > 0); |
| 53 assert(itemRect.height > 0); | 42 assert(itemRect.height > 0); |
| 54 | 43 |
| 55 setState(() { | 44 setState(() { |
| 56 _height = scrollRect.height; | 45 _height = scrollRect.height; |
| 57 _itemHeight = itemRect.height; | 46 _itemHeight = itemRect.height; |
| 58 }); | 47 }); |
| 59 } | 48 } |
| 60 | 49 |
| 61 Node build() { | 50 Node build() { |
| 62 var itemNumber = 0; | 51 var itemNumber = 0; |
| 63 var drawCount = 1; | 52 var drawCount = 1; |
| 64 var transformStyle = ''; | 53 var transformStyle = ''; |
| 65 | 54 |
| 66 if (_height > 0.0) { | 55 if (_height > 0.0) { |
| 67 drawCount = (_height / _itemHeight).round() + 1; | 56 drawCount = (_height / _itemHeight).round() + 1; |
| 68 double alignmentDelta = -_scrollOffset % _itemHeight; | 57 double alignmentDelta = -scrollOffset % _itemHeight; |
| 69 if (alignmentDelta != 0.0) { | 58 if (alignmentDelta != 0.0) { |
| 70 alignmentDelta -= _itemHeight; | 59 alignmentDelta -= _itemHeight; |
| 71 } | 60 } |
| 72 | 61 |
| 73 double drawStart = _scrollOffset + alignmentDelta; | 62 double drawStart = scrollOffset + alignmentDelta; |
| 74 itemNumber = (drawStart / _itemHeight).floor(); | 63 itemNumber = (drawStart / _itemHeight).floor(); |
| 75 | 64 |
| 76 transformStyle = | 65 transformStyle = |
| 77 'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)'; | 66 'transform: translateY(${(alignmentDelta).toStringAsFixed(2)}px)'; |
| 78 } | 67 } |
| 79 | 68 |
| 80 return new Container( | 69 return new Container( |
| 81 styles: [_style], | 70 styles: [_style], |
| 82 children: [ | 71 children: [ |
| 83 new Container( | 72 new Container( |
| 84 styles: [_scrollAreaStyle], | 73 styles: [_scrollAreaStyle], |
| 85 inlineStyle: transformStyle, | 74 inlineStyle: transformStyle, |
| 86 children: buildItems(itemNumber, drawCount) | 75 children: buildItems(itemNumber, drawCount) |
| 87 ) | 76 ) |
| 88 ] | 77 ] |
| 89 ); | 78 ); |
| 90 } | 79 } |
| 91 | |
| 92 void didUnmount() { | |
| 93 _stopFling(); | |
| 94 } | |
| 95 | |
| 96 bool _scrollBy(double scrollDelta) { | |
| 97 var newScrollOffset = _scrollOffset + scrollDelta; | |
| 98 if (minOffset != null && newScrollOffset < minOffset) { | |
| 99 newScrollOffset = minOffset; | |
| 100 } else if (maxOffset != null && newScrollOffset > maxOffset) { | |
| 101 newScrollOffset = maxOffset; | |
| 102 } | |
| 103 if (newScrollOffset == _scrollOffset) { | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 setState(() { | |
| 108 _scrollOffset = newScrollOffset; | |
| 109 }); | |
| 110 return true; | |
| 111 } | |
| 112 | |
| 113 void _scheduleFlingUpdate() { | |
| 114 _flingAnimationId = sky.window.requestAnimationFrame(_updateFling); | |
| 115 } | |
| 116 | |
| 117 void _stopFling() { | |
| 118 if (_flingAnimationId == null) { | |
| 119 return; | |
| 120 } | |
| 121 | |
| 122 sky.window.cancelAnimationFrame(_flingAnimationId); | |
| 123 _flingCurve = null; | |
| 124 _flingAnimationId = null; | |
| 125 } | |
| 126 | |
| 127 void _updateFling(double timeStamp) { | |
| 128 double scrollDelta = _flingCurve.update(timeStamp); | |
| 129 if (!_scrollBy(scrollDelta)) | |
| 130 return _stopFling(); | |
| 131 _scheduleFlingUpdate(); | |
| 132 } | |
| 133 | |
| 134 void _handleScrollUpdate(sky.GestureEvent event) { | |
| 135 _scrollBy(-event.dy); | |
| 136 } | |
| 137 | |
| 138 void _handleFlingStart(sky.GestureEvent event) { | |
| 139 setState(() { | |
| 140 _flingCurve = new FlingCurve(-event.velocityY, event.timeStamp); | |
| 141 _scheduleFlingUpdate(); | |
| 142 }); | |
| 143 } | |
| 144 | |
| 145 void _handleFlingCancel(sky.GestureEvent event) { | |
| 146 _stopFling(); | |
| 147 } | |
| 148 | |
| 149 void _handleWheel(sky.WheelEvent event) { | |
| 150 _scrollBy(-event.offsetY); | |
| 151 } | |
| 152 } | 80 } |
| OLD | NEW |