| OLD | NEW |
| 1 // Copyright (c) 2011, 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 part of base; | 5 part of base; |
| 6 | 6 |
| 7 typedef void AnimationCallback(num currentTime); | 7 typedef void AnimationCallback(num currentTime); |
| 8 | 8 |
| 9 class CallbackData { | 9 class CallbackData { |
| 10 final AnimationCallback callback; | 10 final AnimationCallback callback; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 _nextId = 1; | 21 _nextId = 1; |
| 22 } | 22 } |
| 23 id = _nextId++; | 23 id = _nextId++; |
| 24 } | 24 } |
| 25 } | 25 } |
| 26 | 26 |
| 27 /** | 27 /** |
| 28 * Animation scheduler implementing the functionality provided by | 28 * Animation scheduler implementing the functionality provided by |
| 29 * [:window.requestAnimationFrame:] for platforms that do not support it | 29 * [:window.requestAnimationFrame:] for platforms that do not support it |
| 30 * or support it badly. When multiple UI components are animating at once, | 30 * or support it badly. When multiple UI components are animating at once, |
| 31 * this approach yields superior performance to calling setTimeout/Timer | 31 * this approach yields superior performance to calling setTimeout directly as |
| 32 * directly as all pieces of the UI will animate at the same time resulting in | 32 * all pieces of the UI will animate at the same time resulting in fewer |
| 33 * fewer layouts. | 33 * layouts. |
| 34 */ | 34 */ |
| 35 // TODO(jacobr): use window.requestAnimationFrame when it is available and | 35 // TODO(jacobr): use window.requestAnimationFrame when it is available and |
| 36 // 60fps for the current browser. | 36 // 60fps for the current browser. |
| 37 class AnimationScheduler { | 37 class AnimationScheduler { |
| 38 static const FRAMES_PER_SECOND = 60; | 38 static const FRAMES_PER_SECOND = 60; |
| 39 static const MS_PER_FRAME = 1000 ~/ FRAMES_PER_SECOND; | 39 static const MS_PER_FRAME = 1000 ~/ FRAMES_PER_SECOND; |
| 40 static const USE_INTERVALS = false; | 40 static const USE_INTERVALS = false; |
| 41 | 41 |
| 42 /** List of callbacks to be executed next animation frame. */ | 42 /** List of callbacks to be executed next animation frame. */ |
| 43 List<CallbackData> _callbacks; | 43 List<CallbackData> _callbacks; |
| 44 Timer _timer; | 44 int _intervalId; |
| 45 bool _isMobileSafari = false; | 45 bool _isMobileSafari = false; |
| 46 CssStyleDeclaration _safariHackStyle; | 46 CssStyleDeclaration _safariHackStyle; |
| 47 int _frameCount = 0; | 47 int _frameCount = 0; |
| 48 bool _webkitAnimationFrameMaybeAvailable = true; | 48 bool _webkitAnimationFrameMaybeAvailable = true; |
| 49 | 49 |
| 50 AnimationScheduler() | 50 AnimationScheduler() |
| 51 : _callbacks = new List<CallbackData>() { | 51 : _callbacks = new List<CallbackData>() { |
| 52 if (_isMobileSafari) { | 52 if (_isMobileSafari) { |
| 53 // TODO(jacobr): find a better workaround for the issue that 3d transforms | 53 // TODO(jacobr): find a better workaround for the issue that 3d transforms |
| 54 // sometimes don't render on iOS without forcing a layout. | 54 // sometimes don't render on iOS without forcing a layout. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 77 int requestAnimationFrame(AnimationCallback callback, | 77 int requestAnimationFrame(AnimationCallback callback, |
| 78 [Element element = null, | 78 [Element element = null, |
| 79 num minTime = null]) { | 79 num minTime = null]) { |
| 80 final callbackData = new CallbackData(callback, minTime); | 80 final callbackData = new CallbackData(callback, minTime); |
| 81 _requestAnimationFrameHelper(callbackData); | 81 _requestAnimationFrameHelper(callbackData); |
| 82 return callbackData.id; | 82 return callbackData.id; |
| 83 } | 83 } |
| 84 | 84 |
| 85 void _requestAnimationFrameHelper(CallbackData callbackData) { | 85 void _requestAnimationFrameHelper(CallbackData callbackData) { |
| 86 _callbacks.add(callbackData); | 86 _callbacks.add(callbackData); |
| 87 if (_timer == null) { | 87 if (_intervalId == null) { |
| 88 _setupInterval(); | 88 _setupInterval(); |
| 89 } | 89 } |
| 90 } | 90 } |
| 91 | 91 |
| 92 void _setupInterval() { | 92 void _setupInterval() { |
| 93 // Assert that we never schedule multiple frames at once. | 93 // Assert that we never schedule multiple frames at once. |
| 94 assert(__timer == null); | 94 assert(_intervalId == null); |
| 95 if (USE_INTERVALS) { | 95 if (USE_INTERVALS) { |
| 96 _timer = new Timer.repeating(const Duration(milliseconds: MS_PER_FRAME), | 96 _intervalId = window.setInterval(_step, MS_PER_FRAME); |
| 97 (_) { _step(); }); | |
| 98 } else { | 97 } else { |
| 99 if (_webkitAnimationFrameMaybeAvailable) { | 98 if (_webkitAnimationFrameMaybeAvailable) { |
| 100 try { | 99 try { |
| 101 // TODO(jacobr): passing in document should not be required. | 100 // TODO(jacobr): passing in document should not be required. |
| 102 window.webkitRequestAnimationFrame( | 101 _intervalId = window.webkitRequestAnimationFrame( |
| 103 (int ignored) { _step(); }); | 102 (int ignored) { _step(); }); |
| 104 // TODO(jacobr) fix this odd type error. | 103 // TODO(jacobr) fix this odd type error. |
| 105 } catch (e) { | 104 } catch (e) { |
| 106 _webkitAnimationFrameMaybeAvailable = false; | 105 _webkitAnimationFrameMaybeAvailable = false; |
| 107 } | 106 } |
| 108 } | 107 } |
| 109 if (!_webkitAnimationFrameMaybeAvailable) { | 108 if (!_webkitAnimationFrameMaybeAvailable) { |
| 110 _timer = new Timer(const Duration(milliseconds: MS_PER_FRAME), | 109 _intervalId = window.setTimeout(() { _step(); }, MS_PER_FRAME); |
| 111 _step()); | |
| 112 } | 110 } |
| 113 } | 111 } |
| 114 } | 112 } |
| 115 | 113 |
| 116 void _step() { | 114 void _step() { |
| 117 if (_callbacks.isEmpty) { | 115 if (_callbacks.isEmpty) { |
| 118 // Cancel the interval on the first frame where there aren't actually | 116 // Cancel the interval on the first frame where there aren't actually |
| 119 // any available callbacks. | 117 // any available callbacks. |
| 120 assert(_timer != null); | 118 assert(_intervalId != null); |
| 121 if (USE_INTERVALS) { | 119 if (USE_INTERVALS) { |
| 122 _timer.cancel(); | 120 window.clearInterval(_intervalId); |
| 123 } | 121 } |
| 124 _timer = null; | 122 _intervalId = null; |
| 125 } else if (USE_INTERVALS == false) { | 123 } else if (USE_INTERVALS == false) { |
| 126 _timer = null; | 124 _intervalId = null; |
| 127 _setupInterval(); | 125 _setupInterval(); |
| 128 } | 126 } |
| 129 int numRemaining = 0; | 127 int numRemaining = 0; |
| 130 int minTime = new DateTime.now().millisecondsSinceEpoch + MS_PER_FRAME; | 128 int minTime = new DateTime.now().millisecondsSinceEpoch + MS_PER_FRAME; |
| 131 | 129 |
| 132 int len = _callbacks.length; | 130 int len = _callbacks.length; |
| 133 for (final callback in _callbacks) { | 131 for (final callback in _callbacks) { |
| 134 if (!callback.ready(minTime)) { | 132 if (!callback.ready(minTime)) { |
| 135 numRemaining++; | 133 numRemaining++; |
| 136 } | 134 } |
| (...skipping 24 matching lines...) Expand all Loading... |
| 161 _frameCount++; | 159 _frameCount++; |
| 162 if (_isMobileSafari) { | 160 if (_isMobileSafari) { |
| 163 // Hack to work around an iOS bug where sometimes animations do not | 161 // Hack to work around an iOS bug where sometimes animations do not |
| 164 // render if only webkit transforms were modified. | 162 // render if only webkit transforms were modified. |
| 165 // TODO(jacobr): find a cleaner workaround. | 163 // TODO(jacobr): find a cleaner workaround. |
| 166 int offset = _frameCount % 2; | 164 int offset = _frameCount % 2; |
| 167 _safariHackStyle.left = '${offset}px'; | 165 _safariHackStyle.left = '${offset}px'; |
| 168 } | 166 } |
| 169 } | 167 } |
| 170 } | 168 } |
| OLD | NEW |