OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Implementation of a custom scrolling behavior. | 6 * Implementation of a custom scrolling behavior. |
7 * This behavior overrides native scrolling for an area. This area can be a | 7 * This behavior overrides native scrolling for an area. This area can be a |
8 * single defined part of a page, the entire page, or several different parts | 8 * single defined part of a page, the entire page, or several different parts |
9 * of a page. | 9 * of a page. |
10 * | 10 * |
11 * To use this scrolling behavior you need to define a frame and the content. | 11 * To use this scrolling behavior you need to define a frame and the content. |
(...skipping 13 matching lines...) Expand all Loading... |
25 * For this to work properly you need to set -webkit-text-size-adjust to 'none' | 25 * For this to work properly you need to set -webkit-text-size-adjust to 'none' |
26 * on an ancestor element of the frame, or on the frame itself. If you forget | 26 * on an ancestor element of the frame, or on the frame itself. If you forget |
27 * this you may see the text content of the scrollable area changing size as it | 27 * this you may see the text content of the scrollable area changing size as it |
28 * moves. | 28 * moves. |
29 * | 29 * |
30 * The behavior is intended to support vertical and horizontal scrolling, and | 30 * The behavior is intended to support vertical and horizontal scrolling, and |
31 * scrolling with momentum when a touch gesture flicks with enough velocity. | 31 * scrolling with momentum when a touch gesture flicks with enough velocity. |
32 */ | 32 */ |
33 typedef void Callback(); | 33 typedef void Callback(); |
34 | 34 |
| 35 // Helper method to await the completion of 2 futures. |
| 36 void joinFutures(List<Future> futures, Callback callback) { |
| 37 int count = 0; |
| 38 int len = futures.length; |
| 39 void helper(value) { |
| 40 count++; |
| 41 if (count == len) { |
| 42 callback(); |
| 43 } |
| 44 } |
| 45 for (Future p in futures) { |
| 46 p.then(helper); |
| 47 } |
| 48 } |
| 49 |
35 class Scroller implements Draggable, MomentumDelegate { | 50 class Scroller implements Draggable, MomentumDelegate { |
36 | 51 |
37 /** Pixels to move each time an arrow key is pressed. */ | 52 /** Pixels to move each time an arrow key is pressed. */ |
38 static final ARROW_KEY_DELTA = 30; | 53 static final ARROW_KEY_DELTA = 30; |
39 static final SCROLL_WHEEL_VELOCITY = 0.01; | 54 static final SCROLL_WHEEL_VELOCITY = 0.01; |
40 static final FAST_SNAP_DECELERATION_FACTOR = 0.84; | 55 static final FAST_SNAP_DECELERATION_FACTOR = 0.84; |
41 static final PAGE_KEY_SCROLL_FRACTION = .85; | 56 static final PAGE_KEY_SCROLL_FRACTION = .85; |
42 | 57 |
43 // TODO(jacobr): remove this static variable. | 58 // TODO(jacobr): remove this static variable. |
44 static bool _dragInProgress = false; | 59 static bool _dragInProgress = false; |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 break; | 224 break; |
210 */ | 225 */ |
211 } | 226 } |
212 if (handled) { | 227 if (handled) { |
213 e.preventDefault(); | 228 e.preventDefault(); |
214 } | 229 } |
215 }); | 230 }); |
216 // The scrollable element must be relatively positioned. | 231 // The scrollable element must be relatively positioned. |
217 // TODO(jacobr): this assert fires asynchronously which could be confusing. | 232 // TODO(jacobr): this assert fires asynchronously which could be confusing. |
218 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { | 233 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { |
219 window.requestMeasurementFrame(() { | 234 _element.computedStyle.then((CSSStyleDeclaration style) { |
220 assert(_element.computedStyle.position != "static"); | 235 assert(style.position != "static"); |
221 }); | 236 }); |
222 } | 237 } |
223 | 238 |
224 _initLayer(); | 239 _initLayer(); |
225 } | 240 } |
226 | 241 |
227 EventListenerList get onScrollerStart() { | 242 EventListenerList get onScrollerStart() { |
228 if (_onScrollerStart === null) { | 243 if (_onScrollerStart === null) { |
229 _onScrollerStart = new SimpleEventListenerList(); | 244 _onScrollerStart = new SimpleEventListenerList(); |
230 } | 245 } |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
481 void onTouchEnd() { | 496 void onTouchEnd() { |
482 } | 497 } |
483 | 498 |
484 /** | 499 /** |
485 * Prepare the scrollable area for possible movement. | 500 * Prepare the scrollable area for possible movement. |
486 */ | 501 */ |
487 bool onTouchStart(TouchEvent e) { | 502 bool onTouchStart(TouchEvent e) { |
488 reconfigure(() { | 503 reconfigure(() { |
489 final touch = e.touches[0]; | 504 final touch = e.touches[0]; |
490 if (_momentum.decelerating) { | 505 if (_momentum.decelerating) { |
491 // TODO(jacobr): this won't do any good as it is called too late due | |
492 // to async measurement. | |
493 e.preventDefault(); | 506 e.preventDefault(); |
494 e.stopPropagation(); | 507 e.stopPropagation(); |
495 stop(); | 508 stop(); |
496 } | 509 } |
497 _contentStartOffset = _contentOffset.clone(); | 510 _contentStartOffset = _contentOffset.clone(); |
498 _snapContentOffsetToBounds(); | 511 _snapContentOffsetToBounds(); |
499 }); | 512 }); |
500 return true; | 513 return true; |
501 } | 514 } |
502 | 515 |
(...skipping 18 matching lines...) Expand all Loading... |
521 _minOffset.x = 0; | 534 _minOffset.x = 0; |
522 _minOffset.y = 0; | 535 _minOffset.y = 0; |
523 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y)); | 536 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y)); |
524 } | 537 } |
525 | 538 |
526 /** | 539 /** |
527 * Recalculate dimensions of the frame and the content. Adjust the minPoint | 540 * Recalculate dimensions of the frame and the content. Adjust the minPoint |
528 * and maxPoint allowed for scrolling. | 541 * and maxPoint allowed for scrolling. |
529 */ | 542 */ |
530 void _resize(Callback callback) { | 543 void _resize(Callback callback) { |
531 window.requestMeasurementFrame(() { | 544 final frameRect = _frame.rect; |
532 final offset = _frame.rect.offset; | 545 Future contentSizeFuture; |
533 | 546 |
534 if (_lookupContentSizeDelegate !== null) { | 547 if (_lookupContentSizeDelegate !== null) { |
535 // Guaranteed to be called within a measurement frame | 548 contentSizeFuture = _lookupContentSizeDelegate(); |
536 _contentSize = _lookupContentSizeDelegate(); | 549 contentSizeFuture.then((Size size) { |
537 } else { | 550 _contentSize = size; |
538 final scroll = _element.rect.scroll; | 551 }); |
539 _contentSize = new Size(scroll.width, scroll.height); | 552 } else { |
540 } | 553 contentSizeFuture = _element.rect; |
| 554 contentSizeFuture.then((ElementRect rect) { |
| 555 _contentSize = new Size(rect.scroll.width, rect.scroll.height); |
| 556 }); |
| 557 } |
541 | 558 |
542 _scrollSize = new Size(offset.width, offset.height); | 559 joinFutures(<Future>[frameRect, contentSizeFuture], () { |
| 560 _scrollSize = new Size(frameRect.value.offset.width, |
| 561 frameRect.value.offset.height); |
543 Size adjusted = _getAdjustedContentSize(); | 562 Size adjusted = _getAdjustedContentSize(); |
544 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); | 563 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); |
545 _minPoint = new Coordinate( | 564 _minPoint = new Coordinate( |
546 Math.min( | 565 Math.min( |
547 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), | 566 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), |
548 Math.min( | 567 Math.min( |
549 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y))
; | 568 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y))
; |
550 return callback; | 569 callback(); |
551 }); | 570 }); |
552 } | 571 } |
553 | 572 |
554 Coordinate _snapToBounds(num x, num y) { | 573 Coordinate _snapToBounds(num x, num y) { |
555 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); | 574 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); |
556 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); | 575 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); |
557 return new Coordinate(clampX, clampY); | 576 return new Coordinate(clampX, clampY); |
558 } | 577 } |
559 | 578 |
560 /** | 579 /** |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
703 for (EventListener listener in _listeners) { | 722 for (EventListener listener in _listeners) { |
704 listener(evt); | 723 listener(evt); |
705 } | 724 } |
706 } | 725 } |
707 } | 726 } |
708 | 727 |
709 class ScrollerScrollTechnique { | 728 class ScrollerScrollTechnique { |
710 static final TRANSFORM_3D = 1; | 729 static final TRANSFORM_3D = 1; |
711 static final RELATIVE_POSITIONING = 2; | 730 static final RELATIVE_POSITIONING = 2; |
712 } | 731 } |
OLD | NEW |