| <!doctype html> |
| <meta charset=utf-8> |
| <title>AnimationEffect.updateTiming</title> |
| <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="../../testcommon.js"></script> |
| <script src="../../resources/easing-tests.js"></script> |
| <script src="../../resources/timing-tests.js"></script> |
| <body> |
| <div id="log"></div> |
| <script> |
| 'use strict'; |
| |
| // ------------------------------ |
| // delay |
| // ------------------------------ |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, 100); |
| 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 = createDiv(t).animate(null, 100); |
| 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 negative number'); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, 100); |
| 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 = createDiv(t).animate(null, { fill: 'both', duration: 100 }); |
| 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 = createDiv(t).animate(null, { fill: 'both', duration: 100 }); |
| 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 = createDiv(t).animate(null); |
| assert_throws({ name: 'TypeError' }, () => { |
| anim.effect.updateTiming({ delay: invalid }); |
| }); |
| }, `Throws when setting invalid delay value: ${invalid}`); |
| } |
| |
| |
| // ------------------------------ |
| // endDelay |
| // ------------------------------ |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, 2000); |
| 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 = createDiv(t).animate(null, 2000); |
| 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 = createDiv(t).animate(null, 2000); |
| assert_throws({ name: 'TypeError' }, () => { |
| anim.effect.updateTiming({ endDelay: Infinity }); |
| }); |
| }, 'Throws when setting the endDelay to infinity'); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, 2000); |
| assert_throws({ name: '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 = createDiv(t).animate(null, 100); |
| 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 = createDiv(t).animate(null, |
| { iterationStart: 0.2, |
| iterations: 1, |
| fill: 'both', |
| duration: 100, |
| delay: 1 }); |
| 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 = createDiv(t).animate(null, |
| { iterationStart: 0.2, |
| iterations: 1, |
| fill: 'both', |
| duration: 100, |
| delay: 0 }); |
| 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 = createDiv(t).animate(null, |
| { iterationStart: 0.2, |
| iterations: 1, |
| fill: 'both', |
| duration: 100, |
| delay: 0 }); |
| anim.finish(); |
| anim.effect.updateTiming({ iterationStart: 2.5 }); |
| 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 = createDiv(t).animate(null); |
| assert_throws({ name: 'TypeError' }, () => { |
| anim.effect.updateTiming({ iterationStart: invalid }); |
| }, `setting ${invalid}`); |
| }, `Throws when setting invalid iterationStart value: ${invalid}`); |
| } |
| |
| // ------------------------------ |
| // iterations |
| // ------------------------------ |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, 2000); |
| 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 = createDiv(t).animate(null, 2000); |
| anim.effect.updateTiming({ iterations: Infinity }); |
| assert_equals(anim.effect.getTiming().iterations, Infinity, |
| 'set duration Infinity'); |
| assert_equals(anim.effect.getComputedTiming().iterations, Infinity, |
| 'getComputedTiming() after set iterations Infinity'); |
| }, 'Allows setting iterations to infinity'); |
| |
| for (const invalid of gBadIterationsValues) { |
| test(t => { |
| const anim = createDiv(t).animate(null); |
| assert_throws({ name: 'TypeError' }, () => { |
| anim.effect.updateTiming({ iterations: invalid }); |
| }); |
| }, `Throws when setting invalid iterations value: ${invalid}`); |
| } |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, { duration: 100000, fill: 'both' }); |
| |
| anim.finish(); |
| |
| assert_equals(anim.effect.getComputedTiming().progress, 1, |
| 'progress when animation is finished'); |
| assert_equals(anim.effect.getComputedTiming().currentIteration, 0, |
| 'current iteration when animation is finished'); |
| |
| anim.effect.updateTiming({ iterations: 2 }); |
| |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, |
| 0, |
| 'progress after adding an iteration'); |
| assert_time_equals_literal(anim.effect.getComputedTiming().currentIteration, |
| 1, |
| 'current iteration after adding an iteration'); |
| |
| anim.effect.updateTiming({ iterations: 0 }); |
| |
| assert_equals(anim.effect.getComputedTiming().progress, 0, |
| 'progress after setting iterations to zero'); |
| assert_equals(anim.effect.getComputedTiming().currentIteration, 0, |
| 'current iteration after setting iterations to zero'); |
| |
| anim.effect.updateTiming({ iterations: Infinity }); |
| |
| assert_equals(anim.effect.getComputedTiming().progress, 0, |
| 'progress after setting iterations to Infinity'); |
| assert_equals(anim.effect.getComputedTiming().currentIteration, 1, |
| 'current iteration after setting iterations to Infinity'); |
| }, 'Allows setting the iterations of an animation in progress'); |
| |
| |
| // ------------------------------ |
| // duration |
| // ------------------------------ |
| |
| for (const duration of gGoodDurationValues) { |
| test(t => { |
| const anim = createDiv(t).animate(null, 2000); |
| 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_time_equals_literal(anim.effect.getComputedTiming().duration, |
| duration.computed, |
| 'Updates computed duration'); |
| }, `Allows setting the duration to ${duration.specified}`); |
| } |
| |
| for (const invalid of gBadDurationValues) { |
| test(t => { |
| assert_throws(new TypeError, () => { |
| createDiv(t).animate(null, { duration: invalid }); |
| }); |
| }, 'Throws when setting invalid duration: ' |
| + (typeof invalid === 'string' ? `"${invalid}"` : invalid)); |
| } |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, { duration: 100000, fill: 'both' }); |
| 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, 0.5, |
| '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 = createDiv(t).animate(null, 100 * MS_PER_SEC); |
| return anim.ready.then(() => { |
| const originalStartTime = anim.startTime; |
| const originalCurrentTime = anim.currentTime; |
| assert_time_equals_literal( |
| anim.effect.getComputedTiming().duration, |
| 100 * MS_PER_SEC, |
| 'Initial duration should be as set on KeyframeEffect' |
| ); |
| |
| anim.effect.updateTiming({ duration: 200 * MS_PER_SEC }); |
| assert_time_equals_literal( |
| anim.effect.getComputedTiming().duration, |
| 200 * MS_PER_SEC, |
| 'Effect duration should have been updated' |
| ); |
| assert_times_equal(anim.startTime, originalStartTime, |
| 'startTime should be unaffected by changing effect ' + |
| 'duration'); |
| assert_times_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 = createDiv(t).animate(null, 2000); |
| |
| 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'); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, { |
| duration: 10000, |
| direction: 'normal', |
| }); |
| anim.currentTime = 7000; |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, |
| 'progress before updating direction'); |
| |
| anim.effect.updateTiming({ direction: 'reverse' }); |
| |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, |
| 'progress after updating direction'); |
| }, 'Allows setting the direction of an animation in progress from \'normal\' to' |
| + ' \'reverse\''); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, |
| { duration: 10000, direction: 'normal' }); |
| 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 = createDiv(t).animate(null, |
| { fill: 'backwards', |
| duration: 10000, |
| delay: 10000, |
| direction: 'normal' }); |
| 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'); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, |
| { iterations: 2, |
| duration: 10000, |
| direction: 'normal' }); |
| anim.currentTime = 17000; |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, |
| 'progress before updating direction'); |
| |
| anim.effect.updateTiming({ direction: 'alternate' }); |
| |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, |
| 'progress after updating direction'); |
| }, 'Allows setting the direction of an animation in progress from \'normal\' to' |
| + ' \'alternate\''); |
| |
| test(t => { |
| const anim = createDiv(t).animate(null, |
| { iterations: 2, |
| duration: 10000, |
| direction: 'alternate' }); |
| anim.currentTime = 17000; |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, |
| 'progress before updating direction'); |
| |
| anim.effect.updateTiming({ direction: 'alternate-reverse' }); |
| |
| assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, |
| 'progress after updating direction'); |
| }, 'Allows setting the direction of an animation in progress from \'alternate\'' |
| + ' to \'alternate-reverse\''); |
| |
| |
| // ------------------------------ |
| // easing |
| // ------------------------------ |
| |
| function assert_progress(animation, currentTime, easingFunction) { |
| animation.currentTime = currentTime; |
| const portion = currentTime / animation.effect.getTiming().duration; |
| assert_approx_equals(animation.effect.getComputedTiming().progress, |
| easingFunction(portion), |
| 0.01, |
| 'The progress of the animation should be approximately' |
| + ` ${easingFunction(portion)} at ${currentTime}ms`); |
| } |
| |
| for (const options of gEasingTests) { |
| test(t => { |
| const target = createDiv(t); |
| const anim = target.animate(null, |
| { duration: 1000 * MS_PER_SEC, |
| fill: 'forwards' }); |
| anim.effect.updateTiming({ easing: options.easing }); |
| assert_equals(anim.effect.getTiming().easing, |
| options.serialization || options.easing); |
| |
| const easing = options.easingFunction; |
| assert_progress(anim, 0, easing); |
| assert_progress(anim, 250 * MS_PER_SEC, easing); |
| assert_progress(anim, 500 * MS_PER_SEC, easing); |
| assert_progress(anim, 750 * MS_PER_SEC, easing); |
| assert_progress(anim, 1000 * MS_PER_SEC, easing); |
| }, `Allows setting the easing to a ${options.desc}`); |
| } |
| |
| for (const easing of gRoundtripEasings) { |
| test(t => { |
| const anim = createDiv(t).animate(null); |
| anim.effect.updateTiming({ easing: easing }); |
| assert_equals(anim.effect.getTiming().easing, easing); |
| }, `Updates the specified value when setting the easing to '${easing}'`); |
| } |
| |
| test(t => { |
| const delay = 1000 * MS_PER_SEC; |
| |
| const target = createDiv(t); |
| const anim = target.animate(null, |
| { duration: 1000 * MS_PER_SEC, |
| fill: 'both', |
| delay: delay, |
| easing: 'steps(2, start)' }); |
| |
| anim.effect.updateTiming({ easing: 'steps(2, end)' }); |
| assert_equals(anim.effect.getComputedTiming().progress, 0, |
| 'easing replace to steps(2, end) at before phase'); |
| |
| anim.currentTime = delay + 750 * MS_PER_SEC; |
| 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'); |
| |
| anim.currentTime = delay + 1500 * MS_PER_SEC; |
| 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> |