745f112ef6
Opt an initial batch of files under samples, samples-dev, utils, and runtime/tools/dartfuzz out of null safety in preparation for switching the flag on by default. Change-Id: Icdfd52a5a969e678a7205903332f73fe3841c223 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/166960 Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Leaf Petersen <leafp@google.com>
142 lines
4.3 KiB
Dart
142 lines
4.3 KiB
Dart
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// @dart = 2.9
|
|
|
|
part of base;
|
|
|
|
typedef void AnimationCallback(num currentTime);
|
|
|
|
class CallbackData {
|
|
final AnimationCallback callback;
|
|
final num minTime;
|
|
int id;
|
|
|
|
static int _nextId;
|
|
|
|
bool ready(num time) => minTime == null || minTime <= time;
|
|
|
|
CallbackData(this.callback, this.minTime) {
|
|
// TODO(jacobr): static init needs cleanup, see http://b/4161827
|
|
if (_nextId == null) {
|
|
_nextId = 1;
|
|
}
|
|
id = _nextId++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Animation scheduler implementing the functionality provided by
|
|
* [:window.requestAnimationFrame:] for platforms that do not support it
|
|
* or support it badly. When multiple UI components are animating at once,
|
|
* this approach yields superior performance to calling setTimeout/Timer
|
|
* directly as all pieces of the UI will animate at the same time resulting in
|
|
* fewer layouts.
|
|
*/
|
|
// TODO(jacobr): use window.requestAnimationFrame when it is available and
|
|
// 60fps for the current browser.
|
|
class AnimationScheduler {
|
|
static const FRAMES_PER_SECOND = 60;
|
|
static const MS_PER_FRAME = 1000 ~/ FRAMES_PER_SECOND;
|
|
|
|
/** List of callbacks to be executed next animation frame. */
|
|
List<CallbackData> _callbacks;
|
|
bool _isMobileSafari = false;
|
|
CssStyleDeclaration _safariHackStyle;
|
|
int _frameCount = 0;
|
|
|
|
AnimationScheduler() : _callbacks = new List<CallbackData>() {
|
|
if (_isMobileSafari) {
|
|
// TODO(jacobr): find a better workaround for the issue that 3d transforms
|
|
// sometimes don't render on iOS without forcing a layout.
|
|
final element = new Element.tag('div');
|
|
document.body.nodes.add(element);
|
|
_safariHackStyle = element.style;
|
|
_safariHackStyle.position = 'absolute';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancel the pending callback matching the specified [id].
|
|
* This is not heavily optimized as typically users don't cancel animation
|
|
* frames.
|
|
*/
|
|
void cancelRequestAnimationFrame(int id) {
|
|
_callbacks = _callbacks.where((CallbackData e) => e.id != id).toList();
|
|
}
|
|
|
|
/**
|
|
* Schedule [callback] to execute at the next animation frame that occurs
|
|
* at or after [minTime]. If [minTime] is not specified, the first available
|
|
* animation frame is used. Returns an id that can be used to cancel the
|
|
* pending callback.
|
|
*/
|
|
int requestAnimationFrame(AnimationCallback callback,
|
|
[Element element = null, num minTime = null]) {
|
|
final callbackData = new CallbackData(callback, minTime);
|
|
_requestAnimationFrameHelper(callbackData);
|
|
return callbackData.id;
|
|
}
|
|
|
|
void _requestAnimationFrameHelper(CallbackData callbackData) {
|
|
_callbacks.add(callbackData);
|
|
_setupInterval();
|
|
}
|
|
|
|
void _setupInterval() {
|
|
window.requestAnimationFrame((num ignored) {
|
|
_step();
|
|
});
|
|
}
|
|
|
|
void _step() {
|
|
if (_callbacks.isEmpty) {
|
|
// Cancel the interval on the first frame where there aren't actually
|
|
// any available callbacks.
|
|
} else {
|
|
_setupInterval();
|
|
}
|
|
int numRemaining = 0;
|
|
int minTime = new DateTime.now().millisecondsSinceEpoch + MS_PER_FRAME;
|
|
|
|
int len = _callbacks.length;
|
|
for (final callback in _callbacks) {
|
|
if (!callback.ready(minTime)) {
|
|
numRemaining++;
|
|
}
|
|
}
|
|
|
|
if (numRemaining == len) {
|
|
// TODO(jacobr): we could be more clever about this case if delayed
|
|
// requests really become the main use case...
|
|
return;
|
|
}
|
|
// Some callbacks need to be executed.
|
|
final currentCallbacks = _callbacks;
|
|
_callbacks = new List<CallbackData>();
|
|
|
|
for (final callbackData in currentCallbacks) {
|
|
if (callbackData.ready(minTime)) {
|
|
try {
|
|
(callbackData.callback)(minTime);
|
|
} catch (e) {
|
|
final msg = e.toString();
|
|
print('Suppressed exception ${msg} triggered by callback');
|
|
}
|
|
} else {
|
|
_callbacks.add(callbackData);
|
|
}
|
|
}
|
|
|
|
_frameCount++;
|
|
if (_isMobileSafari) {
|
|
// Hack to work around an iOS bug where sometimes animations do not
|
|
// render if only webkit transforms were modified.
|
|
// TODO(jacobr): find a cleaner workaround.
|
|
int offset = _frameCount % 2;
|
|
_safariHackStyle.left = '${offset}px';
|
|
}
|
|
}
|
|
}
|