| 'use strict'; |
| |
| function registerPassthroughAnimator() { |
| return runInAnimationWorklet(` |
| registerAnimator('passthrough', class { |
| animate(currentTime, effect) { |
| effect.localTime = currentTime; |
| } |
| }); |
| `); |
| } |
| |
| function registerConstantLocalTimeAnimator(localTime) { |
| return runInAnimationWorklet(` |
| registerAnimator('constant_time', class { |
| animate(currentTime, effect) { effect.localTime = ${localTime}; } |
| }); |
| `); |
| } |
| |
| function runInAnimationWorklet(code) { |
| return CSS.animationWorklet.addModule( |
| URL.createObjectURL(new Blob([code], {type: 'text/javascript'})) |
| ); |
| } |
| |
| function approxEquals(actual, expected){ |
| // precision in ms |
| const epsilon = 0.005; |
| const lowerBound = (expected - epsilon) < actual; |
| const upperBound = (expected + epsilon) > actual; |
| return lowerBound && upperBound; |
| } |
| |
| function waitForAsyncAnimationFrames(count) { |
| // In Chrome, waiting for N+1 main thread frames guarantees that compositor has produced |
| // at least N frames. |
| // TODO(majidvp): re-evaluate this choice once other browsers have implemented |
| // AnimationWorklet. |
| return waitForAnimationFrames(count + 1); |
| } |
| |
| async function waitForAnimationFrameWithCondition(condition) { |
| do { |
| await new Promise(window.requestAnimationFrame); |
| } while (!condition()) |
| } |
| |
| async function waitForDocumentTimelineAdvance() { |
| const timeAtStart = document.timeline.currentTime; |
| do { |
| await new Promise(window.requestAnimationFrame); |
| } while (timeAtStart === document.timeline.currentTime) |
| } |
| |
| // Wait until animation's effect has a non-null localTime. |
| async function waitForNotNullLocalTime(animation) { |
| await waitForAnimationFrameWithCondition(_ => { |
| return animation.effect.getComputedTiming().localTime !== null; |
| }); |
| } |