diff options
Diffstat (limited to 'testing/web-platform/tests/scroll-animations/scroll-timelines/effect-updateTiming.html')
-rw-r--r-- | testing/web-platform/tests/scroll-animations/scroll-timelines/effect-updateTiming.html | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scroll-animations/scroll-timelines/effect-updateTiming.html b/testing/web-platform/tests/scroll-animations/scroll-timelines/effect-updateTiming.html new file mode 100644 index 0000000000..78ec8e1de5 --- /dev/null +++ b/testing/web-platform/tests/scroll-animations/scroll-timelines/effect-updateTiming.html @@ -0,0 +1,602 @@ +<!doctype html> +<meta charset=utf-8> +<title>Scroll based animation: AnimationEffect.updateTiming</title> +<!-- Adapted to progressed based scroll animations from "wpt\web-animations\interfaces\AnimationEffect\updateTiming.html" --> +<link rel="help" href="https://drafts.csswg.org/web-animations-1/#dom-animationeffect-updatetiming"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<script src="testcommon.js"></script> +<script src="/web-animations/resources/easing-tests.js"></script> +<script src="/web-animations/resources/timing-tests.js"></script> +<style> + .scroller { + overflow: auto; + height: 100px; + width: 100px; + will-change: transform; + } + .contents { + height: 1000px; + width: 100%; + } +</style> +<body> +<div id="log"></div> +<script> +'use strict'; + +// ------------------------------ +// delay +// ------------------------------ + +promise_test(async t => { + const anim = createScrollLinkedAnimationWithTiming(t, {duration: 1000, delay: 200}) + anim.play(); + + assert_equals(anim.effect.getTiming().delay, 200, 'initial delay 200'); + assert_equals(anim.effect.getComputedTiming().delay, 200, + 'getComputedTiming() initially delay 200'); + + anim.effect.updateTiming({ delay: 100 }); + assert_equals(anim.effect.getTiming().delay, 100, 'set delay 100'); + assert_equals(anim.effect.getComputedTiming().delay, 100, + 'getComputedTiming() after set delay 100'); +}, 'Allows setting the delay to a positive number'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, {duration: 100, delay: -100}) + anim.play(); + anim.effect.updateTiming({ delay: -100 }); + assert_equals(anim.effect.getTiming().delay, -100, 'set delay -100'); + assert_equals(anim.effect.getComputedTiming().delay, -100, + 'getComputedTiming() after set delay -100'); + assert_percents_equal(anim.effect.getComputedTiming().endTime, 0, + 'getComputedTiming().endTime after set delay -100'); +}, 'Allows setting the delay to a negative number'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, {duration: 100}) + anim.play(); + anim.effect.updateTiming({ delay: 100 }); + assert_equals(anim.effect.getComputedTiming().progress, null); + assert_equals(anim.effect.getComputedTiming().currentIteration, null); +}, 'Allows setting the delay of an animation in progress: positive delay that' + + ' causes the animation to be no longer in-effect'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { fill: 'both', duration: 100 }) + anim.play(); + anim.effect.updateTiming({ delay: -50 }); + assert_equals(anim.effect.getComputedTiming().progress, 0.5); +}, 'Allows setting the delay of an animation in progress: negative delay that' + + ' seeks into the active interval'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { fill: 'both', duration: 100 }) + anim.play(); + anim.effect.updateTiming({ delay: -100 }); + assert_equals(anim.effect.getComputedTiming().progress, 1); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0); +}, 'Allows setting the delay of an animation in progress: large negative delay' + + ' that causes the animation to be finished'); + +for (const invalid of gBadDelayValues) { + test(t => { + const anim = createScrollLinkedAnimationWithTiming(t) + anim.play(); + assert_throws_js(TypeError, () => { + anim.effect.updateTiming({ delay: invalid }); + }); + }, `Throws when setting invalid delay value: ${invalid}`); +} + + +// ------------------------------ +// endDelay +// ------------------------------ + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + anim.effect.updateTiming({ endDelay: 123.45 }); + assert_time_equals_literal(anim.effect.getTiming().endDelay, 123.45, + 'set endDelay 123.45'); + assert_time_equals_literal(anim.effect.getComputedTiming().endDelay, 123.45, + 'getComputedTiming() after set endDelay 123.45'); +}, 'Allows setting the endDelay to a positive number'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + anim.effect.updateTiming({ endDelay: -1000 }); + assert_equals(anim.effect.getTiming().endDelay, -1000, 'set endDelay -1000'); + assert_equals(anim.effect.getComputedTiming().endDelay, -1000, + 'getComputedTiming() after set endDelay -1000'); +}, 'Allows setting the endDelay to a negative number'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + assert_throws_js(TypeError, () => { + anim.effect.updateTiming({ endDelay: Infinity }); + }); +}, 'Throws when setting the endDelay to infinity'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + assert_throws_js(TypeError, () => { + anim.effect.updateTiming({ endDelay: -Infinity }); + }); +}, 'Throws when setting the endDelay to negative infinity'); + + +// ------------------------------ +// fill +// ------------------------------ + +for (const fill of ['none', 'forwards', 'backwards', 'both']) { + test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 100 }) + anim.play(); + anim.effect.updateTiming({ fill }); + assert_equals(anim.effect.getTiming().fill, fill, 'set fill ' + fill); + assert_equals(anim.effect.getComputedTiming().fill, fill, + 'getComputedTiming() after set fill ' + fill); + }, `Allows setting the fill to '${fill}'`); +} + + +// ------------------------------ +// iterationStart +// ------------------------------ + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { iterationStart: 0.2, + iterations: 1, + fill: 'both', + duration: 100, + delay: 1 }) + anim.play(); + anim.effect.updateTiming({ iterationStart: 2.5 }); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); + assert_equals(anim.effect.getComputedTiming().currentIteration, 2); +}, 'Allows setting the iterationStart of an animation in progress:' + + ' backwards-filling'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { iterationStart: 0.2, + iterations: 1, + fill: 'both', + duration: 100, + delay: 0 }) + anim.play(); + anim.effect.updateTiming({ iterationStart: 2.5 }); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); + assert_equals(anim.effect.getComputedTiming().currentIteration, 2); +}, 'Allows setting the iterationStart of an animation in progress:' + + ' active phase'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { iterationStart: 0.3, + iterations: 1, + fill: 'both', + duration: 200, + delay: 0 }) + anim.play(); + assert_percents_equal(anim.currentTime, 0); + assert_percents_equal(anim.effect.getComputedTiming().localTime, 0, "localTime"); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0); + + anim.finish(); + assert_percents_equal(anim.currentTime, 100); + assert_percents_equal(anim.effect.getComputedTiming().localTime, 100, "localTime"); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3); + assert_equals(anim.effect.getComputedTiming().currentIteration, 1); + + anim.effect.updateTiming({ iterationStart: 2.5 }); + assert_percents_equal(anim.currentTime, 100); + assert_percents_equal(anim.effect.getComputedTiming().localTime, 100, "localTime"); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); + assert_equals(anim.effect.getComputedTiming().currentIteration, 3); +}, 'Allows setting the iterationStart of an animation in progress:' + + ' forwards-filling'); + +for (const invalid of gBadIterationStartValues) { + test(t => { + const anim = createScrollLinkedAnimationWithTiming(t) + anim.play(); + assert_throws_js(TypeError, () => { + anim.effect.updateTiming({ iterationStart: invalid }); + }, `setting ${invalid}`); + }, `Throws when setting invalid iterationStart value: ${invalid}`); +} + +// ------------------------------ +// iterations +// ------------------------------ + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + anim.effect.updateTiming({ iterations: 2 }); + assert_equals(anim.effect.getTiming().iterations, 2, 'set duration 2'); + assert_equals(anim.effect.getComputedTiming().iterations, 2, + 'getComputedTiming() after set iterations 2'); +}, 'Allows setting iterations to a double value'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + assert_throws_js(TypeError, () => { + anim.effect.updateTiming({ iterations: Infinity }); + }, "test"); +}, `Throws when setting iterations to Infinity`); + + +// progress based animations behave a bit differently than time based animations +// when changing iterations. +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 100000, fill: 'both' }) + anim.play(); + anim.finish(); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress when animation is finished'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 100, + 'duration when animation is finished'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'current iteration when animation is finished'); + + anim.effect.updateTiming({ iterations: 2 }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after adding an iteration'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 50, + 'duration after adding an iteration'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 1, + 'current iteration after adding an iteration'); + + anim.effect.updateTiming({ iterations: 4 }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after setting iterations to 4'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 25, + 'duration after setting iterations to 4'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 3, + 'current iteration after setting iterations to 4'); + + anim.effect.updateTiming({ iterations: 0 }); + + assert_equals(anim.effect.getComputedTiming().progress, 0, + 'progress after setting iterations to zero'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 0, + 'duration after setting iterations to zero'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'current iteration after setting iterations to zero'); +}, 'Allows setting the iterations of an animation in progress'); + +// Added test for checking duration "auto" +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { fill: 'both' }) + anim.play(); + anim.finish(); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress when animation is finished'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 100, + 'duration when animation is finished'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'current iteration when animation is finished'); + + anim.effect.updateTiming({ iterations: 2 }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after adding an iteration'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 50, + 'duration after adding an iteration'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 1, + 'current iteration after adding an iteration'); + + anim.effect.updateTiming({ iterations: 4 }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after setting iterations to 4'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 25, + 'duration after setting iterations to 4'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 3, + 'current iteration after setting iterations to 4'); + + anim.effect.updateTiming({ iterations: 0 }); + + assert_equals(anim.effect.getComputedTiming().progress, 0, + 'progress after setting iterations to zero'); + assert_percents_equal(anim.effect.getComputedTiming().duration, 0, + 'duration after setting iterations to zero'); + assert_equals(anim.effect.getComputedTiming().currentIteration, 0, + 'current iteration after setting iterations to zero'); +}, 'Allows setting the iterations of an animation in progress with duration "auto"'); + + +// ------------------------------ +// duration +// ------------------------------ +// adapted for progress based animations +const gGoodDurationValuesForProgressBased = [ + // until duration returns a CSSNumberish which can handle percentages, 100% + // will be represented as 100 + { specified: 123.45, computed: 100 }, + { specified: 'auto', computed: 100 }, +]; + +for (const duration of gGoodDurationValuesForProgressBased) { + test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, 2000) + anim.play(); + anim.effect.updateTiming({ duration: duration.specified }); + if (typeof duration.specified === 'number') { + assert_time_equals_literal(anim.effect.getTiming().duration, + duration.specified, + 'Updates specified duration'); + } else { + assert_equals(anim.effect.getTiming().duration, duration.specified, + 'Updates specified duration'); + } + assert_percents_equal(anim.effect.getComputedTiming().duration, + duration.computed, + 'Updates computed duration'); + }, `Allows setting the duration to ${duration.specified}`); +} + +// adapted for progress based animations +const gBadDurationValuesForProgressBased = [ + -1, NaN, Infinity, -Infinity, 'abc', '100' +]; + +for (const invalid of gBadDurationValuesForProgressBased) { + test(t => { + assert_throws_js(TypeError, () => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: invalid }) + anim.play(); + }); + }, 'Throws when setting invalid duration: ' + + (typeof invalid === 'string' ? `"${invalid}"` : invalid)); +} + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 100000, fill: 'both' }) + anim.play(); + anim.finish(); + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress when animation is finished'); + anim.effect.updateTiming({ duration: anim.effect.getTiming().duration * 2 }); + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 1, + 'progress after doubling the duration'); + anim.effect.updateTiming({ duration: 0 }); + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after setting duration to zero'); + anim.effect.updateTiming({ duration: 'auto' }); + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after setting duration to \'auto\''); +}, 'Allows setting the duration of an animation in progress'); + +promise_test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 100000, fill: 'both' }) + anim.play(); + return anim.ready.then(() => { + const originalStartTime = anim.startTime; + const originalCurrentTime = anim.currentTime; + assert_percents_equal( + anim.effect.getComputedTiming().duration, + 100, + 'Initial duration should be as set on KeyframeEffect' + ); + + anim.effect.updateTiming({ duration: 200000 }); + assert_percents_equal( + anim.effect.getComputedTiming().duration, + 100, + 'Effect duration should remain at 100% for progress based animations' + ); + assert_percents_equal(anim.startTime, originalStartTime, + 'startTime should be unaffected by changing effect ' + + 'duration'); + + assert_percents_equal(anim.currentTime, originalCurrentTime, + 'currentTime should be unaffected by changing ' + + 'effect duration'); + }); +}, 'Allows setting the duration of an animation in progress such that the' + + ' the start and current time do not change'); + + +// ------------------------------ +// direction +// ------------------------------ + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 2000 }) + anim.play(); + + const directions = ['normal', 'reverse', 'alternate', 'alternate-reverse']; + for (const direction of directions) { + anim.effect.updateTiming({ direction: direction }); + assert_equals(anim.effect.getTiming().direction, direction, + `set direction to ${direction}`); + } +}, 'Allows setting the direction to each of the possible keywords'); + +promise_test(async t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 10000, direction: 'normal' }); + + const scroller = anim.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + anim.play(); + await anim.ready; + scroller.scrollTop = maxScroll * 0.07 + await waitForNextFrame(); + + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.07, + 'progress before updating direction'); + + anim.effect.updateTiming({ direction: 'reverse' }); + + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.93, + 'progress after updating direction'); +}, 'Allows setting the direction of an animation in progress from \'normal\' to' + + ' \'reverse\''); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 10000, direction: 'normal' }); + anim.play(); + assert_equals(anim.effect.getComputedTiming().progress, 0, + 'progress before updating direction'); + + anim.effect.updateTiming({ direction: 'reverse' }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after updating direction'); +}, 'Allows setting the direction of an animation in progress from \'normal\' to' + + ' \'reverse\' while at start of active interval'); + +test(t => { + const anim = createScrollLinkedAnimationWithTiming(t, { fill: 'backwards', + duration: 10000, + delay: 10000, + direction: 'normal' }); + anim.play(); + assert_equals(anim.effect.getComputedTiming().progress, 0, + 'progress before updating direction'); + + anim.effect.updateTiming({ direction: 'reverse' }); + + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'progress after updating direction'); +}, 'Allows setting the direction of an animation in progress from \'normal\' to' + + ' \'reverse\' while filling backwards'); + +promise_test(async t => { + const anim = createScrollLinkedAnimationWithTiming(t, { iterations: 2, + duration: 10000, + direction: 'normal' }); + const scroller = anim.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + anim.play(); + await anim.ready; + scroller.scrollTop = maxScroll * 0.17 // 34% through first iteration + await waitForNextFrame(); + + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.34, + 'progress before updating direction'); + + anim.effect.updateTiming({ direction: 'alternate' }); + + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.34, + 'progress after updating direction'); +}, 'Allows setting the direction of an animation in progress from \'normal\' to' + + ' \'alternate\''); + +promise_test(async t => { + const anim = createScrollLinkedAnimationWithTiming(t, { iterations: 2, + duration: 10000, + direction: 'alternate' }); + const scroller = anim.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + anim.play(); + await anim.ready; + scroller.scrollTop = maxScroll * 0.17 + await waitForNextFrame(); + // anim.currentTime = 17000; // 34% through first iteration + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.34, + 'progress before updating direction'); + + anim.effect.updateTiming({ direction: 'alternate-reverse' }); + + assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.66, + 'progress after updating direction'); +}, 'Allows setting the direction of an animation in progress from \'alternate\'' + + ' to \'alternate-reverse\''); + + +// ------------------------------ +// easing +// ------------------------------ + +async function assert_progress(animation, scrollPercent, easingFunction) { + const scroller = animation.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + scroller.scrollTop = maxScroll * scrollPercent + await waitForNextFrame(); + assert_approx_equals(animation.effect.getComputedTiming().progress, + easingFunction(scrollPercent), + 0.01, + 'The progress of the animation should be approximately' + + ` ${easingFunction(scrollPercent)} at ${scrollPercent}%`); +} + +for (const options of gEasingTests) { + promise_test(async t => { + const anim = createScrollLinkedAnimationWithTiming(t, { duration: 100000, + fill: 'forwards' }); + anim.play(); + anim.effect.updateTiming({ easing: options.easing }); + assert_equals(anim.effect.getTiming().easing, + options.serialization || options.easing); + + const easing = options.easingFunction; + await assert_progress(anim, 0, easing); + await assert_progress(anim, 0.25, easing); + await assert_progress(anim, 0.5, easing); + await assert_progress(anim, 0.75, easing); + await assert_progress(anim, 1, easing); + }, `Allows setting the easing to a ${options.desc}`); +} + +for (const easing of gRoundtripEasings) { + test(t => { + const anim = createScrollLinkedAnimationWithTiming(t); + anim.play(); + anim.effect.updateTiming({ easing: easing }); + assert_equals(anim.effect.getTiming().easing, easing); + }, `Updates the specified value when setting the easing to '${easing}'`); +} + +// Because of the delay being so large, this progress based animation is always +// in the finished state with progress 1. Included here because it is in the +// original test file for time based animations. +promise_test(async t => { + const delay = 1000000; + + const anim = createScrollLinkedAnimationWithTiming(t, + { duration: 1000000, fill: 'both', delay: delay, easing: 'steps(2, start)' }); + + const scroller = anim.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + anim.play(); + await anim.ready; + + anim.effect.updateTiming({ easing: 'steps(2, end)' }); + assert_equals(anim.effect.getComputedTiming().progress, 0, + 'easing replace to steps(2, end) at before phase'); + + scroller.scrollTop = maxScroll * 0.875 + await waitForNextFrame(); + + assert_equals(anim.effect.getComputedTiming().progress, 0.5, + 'change currentTime to active phase'); + + anim.effect.updateTiming({ easing: 'steps(2, start)' }); + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'easing replace to steps(2, start) at active phase'); + + scroller.scrollTop = maxScroll * 1.25 + await waitForNextFrame(); + + anim.effect.updateTiming({ easing: 'steps(2, end)' }); + assert_equals(anim.effect.getComputedTiming().progress, 1, + 'easing replace to steps(2, end) again at after phase'); +}, 'Allows setting the easing of an animation in progress'); +</script> +</body> |