blob: 6a340c0bf47a298d3c04340e91d8026680aa9ba1 [file] [log] [blame]
<!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_js(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_js(TypeError, () => {
anim.effect.updateTiming({ endDelay: Infinity });
});
}, 'Throws when setting the endDelay to infinity');
test(t => {
const anim = createDiv(t).animate(null, 2000);
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 = 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_js(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_js(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_js(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>