| 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 |