summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html')
-rw-r--r--testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html429
1 files changed, 429 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html b/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html
new file mode 100644
index 0000000000..5813de60fa
--- /dev/null
+++ b/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html
@@ -0,0 +1,429 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Setting the timeline of scroll animation</title>
+<link rel="help"
+ href="https://drafts.csswg.org/web-animations-1/#setting-the-timeline">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="testcommon.js"></script>
+<style>
+ .scroller {
+ overflow-x: hidden;
+ overflow-y: auto;
+ height: 200px;
+ width: 100px;
+ will-change: transform;
+ }
+ .contents {
+ /* The height is set to align scrolling in pixels with logical time in ms */
+ height: 1200px;
+ width: 100%;
+ }
+ @keyframes anim {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+ .anim {
+ animation: anim 1s paused linear;
+ }
+ #target {
+ height: 100px;
+ width: 100px;
+ background-color: green;
+ margin-top: -1000px;
+ }
+</style>
+<body>
+<script>
+'use strict';
+
+function createAnimation(t) {
+ const elem = createDiv(t);
+ const animation = elem.animate({ opacity: [1, 0] }, 1000);
+ return animation;
+}
+
+function createPausedCssAnimation(t) {
+ const elem = createDiv(t);
+ elem.classList.add('anim');
+ return elem.getAnimations()[0];
+}
+
+function updateScrollPosition(timeline, offset) {
+ const scroller = timeline.source;
+ assert_true(!!scroller, 'source is resolved');
+ scroller.scrollTop = offset;
+ // Wait for new animation frame which allows the timeline to compute new
+ // current time.
+ return waitForNextFrame();
+}
+
+function assert_timeline_current_time(animation, timeline_current_time) {
+ if (animation.currentTime instanceof CSSUnitValue){
+ assert_percents_equal(animation.timeline.currentTime, timeline_current_time,
+ `Timeline's currentTime aligns with the scroll ` +
+ `position even when paused`);
+ }
+ else {
+ assert_times_equal(animation.timeline.currentTime, timeline_current_time,
+ `Timeline's currentTime aligns with the scroll ` +
+ `position even when paused`);
+ }
+}
+
+function assert_scroll_synced_times(animation, timeline_current_time,
+ animation_current_time) {
+ assert_timeline_current_time(animation, timeline_current_time);
+ if (animation.currentTime instanceof CSSUnitValue){
+ assert_percents_equal(animation.currentTime, animation_current_time,
+ `Animation's currentTime aligns with the scroll position`);
+ }
+ else {
+ assert_times_equal(animation.currentTime, animation_current_time,
+ `Animation's currentTime aligns with the scroll position`);
+ }
+}
+
+function assert_paused_times(animation, timeline_current_time,
+ animation_current_time) {
+ assert_timeline_current_time(animation, timeline_current_time);
+ if (animation.currentTime instanceof CSSUnitValue){
+ assert_percents_equal(animation.currentTime, animation_current_time,
+ `Animation's currentTime is fixed while paused`);
+ }
+ else {
+ assert_times_equal(animation.currentTime, animation_current_time,
+ `Animation's currentTime is fixed while paused`);
+ }
+}
+
+function createViewTimeline(t) {
+ const parent = document.querySelector('.scroller');
+ const elem = document.createElement('div');
+ elem.id = 'target';
+ t.add_cleanup(() => {
+ elem.remove();
+ });
+ parent.appendChild(elem);
+ return new ViewTimeline({ subject: elem });
+}
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.timeline = scrollTimeline;
+ assert_true(animation.pending);
+ await animation.ready;
+
+ assert_equals(animation.playState, 'running');
+ assert_scroll_synced_times(animation, 10, 10);
+}, 'Setting a scroll timeline on a play-pending animation synchronizes ' +
+ 'currentTime of the animation with the scroll position.');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.pause();
+ animation.timeline = scrollTimeline;
+ assert_true(animation.pending);
+ await animation.ready;
+
+ assert_equals(animation.playState, 'paused');
+ assert_paused_times(animation, 10, 0);
+
+ await updateScrollPosition(animation.timeline, 200);
+
+ assert_equals(animation.playState, 'paused');
+ assert_paused_times(animation, 20, 0);
+
+ animation.play();
+ await animation.ready;
+
+ assert_scroll_synced_times(animation, 20, 20);
+}, 'Setting a scroll timeline on a pause-pending animation fixes the ' +
+ 'currentTime of the animation based on the scroll position once resumed');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.reverse();
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+
+ assert_equals(animation.playState, 'running');
+ assert_scroll_synced_times(animation, 10, 90);
+}, 'Setting a scroll timeline on a reversed play-pending animation ' +
+ 'synchronizes the currentTime of the animation with the scroll ' +
+ 'position.');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ await animation.ready;
+
+ animation.timeline = scrollTimeline;
+ assert_true(animation.pending);
+ assert_equals(animation.playState, 'running');
+ await animation.ready;
+ assert_scroll_synced_times(animation, 10, 10);
+}, 'Setting a scroll timeline on a running animation synchronizes the ' +
+ 'currentTime of the animation with the scroll position.');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.pause();
+ await animation.ready;
+
+ animation.timeline = scrollTimeline;
+ assert_false(animation.pending);
+ assert_equals(animation.playState, 'paused');
+ assert_paused_times(animation, 10, 0);
+
+ animation.play();
+ await animation.ready;
+
+ assert_scroll_synced_times(animation, 10, 10);
+}, 'Setting a scroll timeline on a paused animation fixes the currentTime of ' +
+ 'the animation based on the scroll position when resumed');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.reverse();
+ animation.pause();
+ await animation.ready;
+
+ animation.timeline = scrollTimeline;
+ assert_false(animation.pending);
+ assert_equals(animation.playState, 'paused');
+ assert_paused_times(animation, 10, 100);
+
+ animation.play();
+ await animation.ready;
+
+ assert_scroll_synced_times(animation, 10, 90);
+}, 'Setting a scroll timeline on a reversed paused animation ' +
+ 'fixes the currentTime of the animation based on the scroll ' +
+ 'position when resumed');
+
+promise_test(async t => {
+ const animation = createAnimation(t);
+ const scrollTimeline = createScrollTimeline(t);
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+ await updateScrollPosition(scrollTimeline, 100);
+
+ animation.timeline = document.timeline;
+ assert_times_equal(animation.currentTime, 100);
+}, 'Transitioning from a scroll timeline to a document timeline on a running ' +
+ 'animation preserves currentTime');
+
+promise_test(async t => {
+ const animation = createAnimation(t);
+ const scrollTimeline = createScrollTimeline(t);
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+ await updateScrollPosition(scrollTimeline, 100);
+
+ animation.pause();
+ animation.timeline = document.timeline;
+
+ await animation.ready;
+ assert_times_equal(animation.currentTime, 100);
+}, 'Transitioning from a scroll timeline to a document timeline on a ' +
+ 'pause-pending animation preserves currentTime');
+
+promise_test(async t => {
+ const animation = createAnimation(t);
+ const scrollTimeline = createScrollTimeline(t);
+ animation.timeline = scrollTimeline;
+
+ animation.reverse();
+ await animation.ready;
+ await updateScrollPosition(scrollTimeline, 100);
+
+ animation.pause();
+ await animation.ready;
+
+ assert_scroll_synced_times(animation, 10, 90);
+
+ animation.timeline = document.timeline;
+ assert_false(animation.pending);
+ assert_equals(animation.playState, 'paused');
+ assert_times_equal(animation.currentTime, 900);
+}, 'Transition from a scroll timeline to a document timeline on a reversed ' +
+ 'paused animation maintains correct currentTime');
+
+promise_test(async t => {
+ const animation = createAnimation(t);
+ const scrollTimeline = createScrollTimeline(t);
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const progress = animation.currentTime.value / 100;
+ const duration = animation.effect.getTiming().duration;
+ animation.timeline = null;
+
+ const expectedCurrentTime = progress * duration;
+ assert_times_equal(animation.currentTime, expectedCurrentTime);
+}, 'Transitioning from a scroll timeline to a null timeline on a running ' +
+ 'animation preserves current progress.');
+
+promise_test(async t => {
+ const keyframeEfect = new KeyframeEffect(createDiv(t),
+ { opacity: [0, 1] },
+ 1000);
+ const animation = new Animation(keyframeEfect, null);
+ animation.startTime = 0;
+ assert_equals(animation.playState, 'running');
+
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ animation.timeline = scrollTimeline;
+ assert_equals(animation.playState, 'running');
+ await animation.ready;
+
+ assert_percents_equal(animation.currentTime, 10);
+}, 'Switching from a null timeline to a scroll timeline on an animation with ' +
+ 'a resolved start time preserved the play state');
+
+promise_test(async t => {
+ const firstScrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(firstScrollTimeline, 100);
+
+ const secondScrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(secondScrollTimeline, 200);
+
+ const animation = createAnimation(t);
+ animation.timeline = firstScrollTimeline;
+ await animation.ready;
+ assert_percents_equal(animation.currentTime, 10);
+
+ animation.timeline = secondScrollTimeline;
+ await animation.ready;
+
+ assert_percents_equal(animation.currentTime, 20);
+}, 'Switching from one scroll timeline to another updates currentTime');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createPausedCssAnimation(t);
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+ assert_equals(animation.playState, 'paused');
+ assert_percents_equal(animation.currentTime, 0);
+
+ const target = animation.effect.target;
+ target.style.animationPlayState = 'running';
+ await animation.ready;
+
+ assert_percents_equal(animation.currentTime, 10);
+}, 'Switching from a document timeline to a scroll timeline updates ' +
+ 'currentTime when unpaused via CSS.');
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+
+ const animation = createAnimation(t);
+ animation.pause();
+ animation.currentTime = 500; // 50%
+ animation.timeline = scrollTimeline;
+ await animation.ready;
+ assert_percents_equal(animation.currentTime, 50);
+
+ animation.play();
+ await animation.ready;
+ assert_percents_equal(animation.currentTime, 10);
+}, 'Switching from a document timeline to a scroll timeline and updating ' +
+ 'currentTime preserves the progress while paused.');
+
+promise_test(async t => {
+ const elem = createDiv(t);
+ const animation = elem.animate(null, Infinity);
+ await animation.ready;
+
+ animation.timeline = new ScrollTimeline();
+ let timing = animation.effect.getComputedTiming();
+ assert_percents_equal(timing.endTime, 100);
+ assert_percents_equal(timing.activeDuration, 100);
+ assert_percents_equal(timing.duration, 100);
+
+ animation.effect.updateTiming({ iterations: 2 });
+ timing = animation.effect.getComputedTiming();
+ assert_percents_equal(timing.endTime, 100);
+ assert_percents_equal(timing.activeDuration, 100);
+ assert_percents_equal(timing.duration, 50);
+
+ // Blink implementation does not permit setting an infinite number of
+ // iterations on a scroll-linked animation. Workaround by temporarily
+ // switching back to a document timeline.
+ animation.timeline = document.timeline;
+ animation.effect.updateTiming({ iterations: Infinity });
+ animation.timeline = new ScrollTimeline();
+ timing = animation.effect.getComputedTiming();
+ // Having an infinite number of iterations with a finite timeline results in
+ // each iteration having zero duration.
+ assert_percents_equal(timing.duration, 0);
+ // If either the iteration duration or iteration count is zero, the active
+ // duration is always zero.
+ assert_percents_equal(timing.activeDuration, 0);
+ assert_percents_equal(timing.endTime, 0);
+
+}, 'Switching from a document timeline to a scroll timeline on an infinite ' +
+ 'duration animation.');
+
+
+promise_test(async t => {
+ const scrollTimeline = createScrollTimeline(t);
+ const view_timeline = createViewTimeline(t);
+ await updateScrollPosition(scrollTimeline, 100);
+ const animation = createAnimation(t);
+ animation.timeline = scrollTimeline;
+ // Range name is ignored while attached to a non-view scroll-timeline.
+ // Offsets are still applied to the scroll-timeline.
+ animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
+ animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
+ await animation.ready;
+
+ assert_scroll_synced_times(animation, 10, 0);
+ assert_percents_equal(animation.startTime, 10);
+
+ animation.timeline = view_timeline;
+ assert_true(animation.pending);
+ await animation.ready;
+
+ // Cover range is [0px, 300px]
+ // Contain range is [100px, 200px]
+ // start time = (contain 10% pos - cover start pos) / cover range * 100%
+ const expected_start_time = 110 / 300 * 100;
+ // timeline time = (scroll pos - cover start pos) / cover range * 100%
+ const expected_timeline_time = 100 / 300 * 100;
+ // current time = timeline time - start time.
+ const expected_current_time = expected_timeline_time - expected_start_time;
+
+ assert_percents_equal(animation.startTime, expected_start_time);
+ assert_percents_equal(animation.timeline.currentTime, expected_timeline_time);
+ assert_percents_equal(animation.currentTime, expected_current_time);
+}, 'Changing from a scroll-timeline to a view-timeline updates start time.');
+
+</script>
+</body>