OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 | |
50 class Scroller implements Draggable, MomentumDelegate { | 35 class Scroller implements Draggable, MomentumDelegate { |
51 | 36 |
52 /** Pixels to move each time an arrow key is pressed. */ | 37 /** Pixels to move each time an arrow key is pressed. */ |
53 static final ARROW_KEY_DELTA = 30; | 38 static final ARROW_KEY_DELTA = 30; |
54 static final SCROLL_WHEEL_VELOCITY = 0.01; | 39 static final SCROLL_WHEEL_VELOCITY = 0.01; |
55 static final FAST_SNAP_DECELERATION_FACTOR = 0.84; | 40 static final FAST_SNAP_DECELERATION_FACTOR = 0.84; |
56 static final PAGE_KEY_SCROLL_FRACTION = .85; | 41 static final PAGE_KEY_SCROLL_FRACTION = .85; |
57 | 42 |
58 // TODO(jacobr): remove this static variable. | 43 // TODO(jacobr): remove this static variable. |
59 static bool _dragInProgress = false; | 44 static bool _dragInProgress = false; |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 break; | 209 break; |
225 */ | 210 */ |
226 } | 211 } |
227 if (handled) { | 212 if (handled) { |
228 e.preventDefault(); | 213 e.preventDefault(); |
229 } | 214 } |
230 }); | 215 }); |
231 // The scrollable element must be relatively positioned. | 216 // The scrollable element must be relatively positioned. |
232 // TODO(jacobr): this assert fires asynchronously which could be confusing. | 217 // TODO(jacobr): this assert fires asynchronously which could be confusing. |
233 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { | 218 if (_scrollTechnique == ScrollerScrollTechnique.RELATIVE_POSITIONING) { |
234 _element.computedStyle.then((CSSStyleDeclaration style) { | 219 window.requestMeasurementFrame(() { |
235 assert(style.position != "static"); | 220 assert(_element.computedStyle.position != "static"); |
236 }); | 221 }); |
237 } | 222 } |
238 | 223 |
239 _initLayer(); | 224 _initLayer(); |
240 } | 225 } |
241 | 226 |
242 EventListenerList get onScrollerStart() { | 227 EventListenerList get onScrollerStart() { |
243 if (_onScrollerStart === null) { | 228 if (_onScrollerStart === null) { |
244 _onScrollerStart = new SimpleEventListenerList(); | 229 _onScrollerStart = new SimpleEventListenerList(); |
245 } | 230 } |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 void onTouchEnd() { | 481 void onTouchEnd() { |
497 } | 482 } |
498 | 483 |
499 /** | 484 /** |
500 * Prepare the scrollable area for possible movement. | 485 * Prepare the scrollable area for possible movement. |
501 */ | 486 */ |
502 bool onTouchStart(TouchEvent e) { | 487 bool onTouchStart(TouchEvent e) { |
503 reconfigure(() { | 488 reconfigure(() { |
504 final touch = e.touches[0]; | 489 final touch = e.touches[0]; |
505 if (_momentum.decelerating) { | 490 if (_momentum.decelerating) { |
| 491 // TODO(jacobr): this won't do any good as it is called too late due |
| 492 // to async measurement. |
506 e.preventDefault(); | 493 e.preventDefault(); |
507 e.stopPropagation(); | 494 e.stopPropagation(); |
508 stop(); | 495 stop(); |
509 } | 496 } |
510 _contentStartOffset = _contentOffset.clone(); | 497 _contentStartOffset = _contentOffset.clone(); |
511 _snapContentOffsetToBounds(); | 498 _snapContentOffsetToBounds(); |
512 }); | 499 }); |
513 return true; | 500 return true; |
514 } | 501 } |
515 | 502 |
(...skipping 18 matching lines...) Expand all Loading... |
534 _minOffset.x = 0; | 521 _minOffset.x = 0; |
535 _minOffset.y = 0; | 522 _minOffset.y = 0; |
536 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y)); | 523 reconfigure(() => _setContentOffset(_maxPoint.x, _maxPoint.y)); |
537 } | 524 } |
538 | 525 |
539 /** | 526 /** |
540 * Recalculate dimensions of the frame and the content. Adjust the minPoint | 527 * Recalculate dimensions of the frame and the content. Adjust the minPoint |
541 * and maxPoint allowed for scrolling. | 528 * and maxPoint allowed for scrolling. |
542 */ | 529 */ |
543 void _resize(Callback callback) { | 530 void _resize(Callback callback) { |
544 final frameRect = _frame.rect; | 531 window.requestMeasurementFrame(() { |
545 Future contentSizeFuture; | 532 final offset = _frame.rect.offset; |
546 | 533 |
547 if (_lookupContentSizeDelegate !== null) { | 534 if (_lookupContentSizeDelegate !== null) { |
548 contentSizeFuture = _lookupContentSizeDelegate(); | 535 // Guaranteed to be called within a measurement frame |
549 contentSizeFuture.then((Size size) { | 536 _contentSize = _lookupContentSizeDelegate(); |
550 _contentSize = size; | 537 } else { |
551 }); | 538 final scroll = _element.rect.scroll; |
552 } else { | 539 _contentSize = new Size(scroll.width, scroll.height); |
553 contentSizeFuture = _element.rect; | 540 } |
554 contentSizeFuture.then((ElementRect rect) { | |
555 _contentSize = new Size(rect.scroll.width, rect.scroll.height); | |
556 }); | |
557 } | |
558 | 541 |
559 joinFutures(<Future>[frameRect, contentSizeFuture], () { | 542 _scrollSize = new Size(offset.width, offset.height); |
560 _scrollSize = new Size(frameRect.value.offset.width, | |
561 frameRect.value.offset.height); | |
562 Size adjusted = _getAdjustedContentSize(); | 543 Size adjusted = _getAdjustedContentSize(); |
563 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); | 544 _maxPoint = new Coordinate(-_maxOffset.x, -_maxOffset.y); |
564 _minPoint = new Coordinate( | 545 _minPoint = new Coordinate( |
565 Math.min( | 546 Math.min( |
566 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), | 547 _scrollSize.width - adjusted.width + _minOffset.x, _maxPoint.x), |
567 Math.min( | 548 Math.min( |
568 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y))
; | 549 _scrollSize.height - adjusted.height + _minOffset.y, _maxPoint.y))
; |
569 callback(); | 550 return callback; |
570 }); | 551 }); |
571 } | 552 } |
572 | 553 |
573 Coordinate _snapToBounds(num x, num y) { | 554 Coordinate _snapToBounds(num x, num y) { |
574 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); | 555 num clampX = GoogleMath.clamp(_minPoint.x, x, _maxPoint.x); |
575 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); | 556 num clampY = GoogleMath.clamp(_minPoint.y, y, _maxPoint.y); |
576 return new Coordinate(clampX, clampY); | 557 return new Coordinate(clampX, clampY); |
577 } | 558 } |
578 | 559 |
579 /** | 560 /** |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 for (EventListener listener in _listeners) { | 703 for (EventListener listener in _listeners) { |
723 listener(evt); | 704 listener(evt); |
724 } | 705 } |
725 } | 706 } |
726 } | 707 } |
727 | 708 |
728 class ScrollerScrollTechnique { | 709 class ScrollerScrollTechnique { |
729 static final TRANSFORM_3D = 1; | 710 static final TRANSFORM_3D = 1; |
730 static final RELATIVE_POSITIONING = 2; | 711 static final RELATIVE_POSITIONING = 2; |
731 } | 712 } |
OLD | NEW |