blob: 95c772e30a8ca7ed4a68941540a2df77b5c25c43 [file] [log] [blame]
<!DOCTYPE html>
<meta charset=utf-8>
<title>Reversing an animation</title>
<link rel="help"
href="https://drafts.csswg.org/web-animations/#reversing-an-animation-section">
<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: auto;
height: 100px;
width: 100px;
will-change: transform;
}
.contents {
height: 1000px;
width: 100%;
}
</style>
<body>
<script>
'use strict';
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
await animation.ready;
animation.currentTime = 500;
const previousPlaybackRate = animation.playbackRate;
animation.reverse();
assert_equals(animation.playbackRate, previousPlaybackRate,
'Playback rate should not have changed');
await animation.ready;
assert_equals(animation.playbackRate, -previousPlaybackRate,
'Playback rate should be inverted');
}, 'Reversing an animation inverts the playback rate');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = 500;
animation.reverse();
assert_equals(animation.currentTime, 500,
'The current time should not change it is in the middle of ' +
'the animation duration');
}, 'Reversing an animation maintains the same current time');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
assert_true(animation.pending,
'The animation is pending before we call reverse');
animation.reverse();
assert_true(animation.pending,
'The animation is still pending after calling reverse');
}, 'Reversing an animation does not cause it to leave the pending state');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
let readyResolved = false;
animation.ready.then(() => { readyResolved = true; });
animation.reverse();
await Promise.resolve();
assert_false(readyResolved,
'ready promise should not have been resolved yet');
}, 'Reversing an animation does not cause it to resolve the ready promise');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = 2000;
animation.reverse();
assert_equals(animation.currentTime, 1000,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime > effect end');
}, 'Reversing an animation when playbackRate > 0 and currentTime > ' +
'effect end should make it play from the end');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = -2000;
animation.reverse();
assert_equals(animation.currentTime, 1000,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime < 0');
}, 'Reversing an animation when playbackRate > 0 and currentTime < 0 ' +
'should make it play from the end');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.playbackRate = -1;
animation.currentTime = -2000;
animation.reverse();
assert_times_equal(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0');
}, 'Reversing an animation when playbackRate < 0 and currentTime < 0 ' +
'should make it play from the start');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.playbackRate = -1;
animation.currentTime = 2000;
animation.reverse();
assert_times_equal(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime > effect end');
}, 'Reversing an animation when playbackRate < 0 and currentTime > effect ' +
'end should make it play from the start');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
animation.effect.updateTiming({ iterations: Infinity });
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = -2000;
assert_throws_dom('InvalidStateError',
() => { animation.reverse(); },
'reverse() should throw InvalidStateError ' +
'if the playbackRate > 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'Reversing an animation when playbackRate > 0 and currentTime < 0 ' +
'and the target effect end is positive infinity should throw an exception');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
animation.effect.updateTiming({ iterations: Infinity });
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = -2000;
try { animation.reverse(); } catch(e) { }
assert_equals(animation.playbackRate, 1, 'playbackRate is unchanged');
await animation.ready;
assert_equals(animation.playbackRate, 1, 'playbackRate remains unchanged');
}, 'When reversing throws an exception, the playback rate remains unchanged');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
animation.effect.updateTiming({ iterations: Infinity });
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = -2000;
animation.playbackRate = 0;
try {
animation.reverse();
} catch (e) {
assert_unreached(`Unexpected exception when calling reverse(): ${e}`);
}
}, 'Reversing animation when playbackRate = 0 and currentTime < 0 ' +
'and the target effect end is positive infinity should NOT throw an ' +
'exception');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
animation.effect.updateTiming({ iterations: Infinity });
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.playbackRate = -1;
animation.currentTime = -2000;
animation.reverse();
assert_times_equal(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'Reversing an animation when playbackRate < 0 and currentTime < 0 ' +
'and the target effect end is positive infinity should make it play ' +
'from the start');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.playbackRate = 0;
animation.currentTime = 500;
animation.reverse();
await animation.ready;
assert_equals(animation.playbackRate, 0,
'reverse() should preserve playbackRate if the playbackRate == 0');
assert_times_equal(animation.currentTime, 500,
'reverse() should not affect the currentTime if the playbackRate == 0');
}, 'Reversing when when playbackRate == 0 should preserve the current ' +
'time and playback rate');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
assert_equals(animation.currentTime, null);
animation.reverse();
await animation.ready;
assert_times_equal(animation.startTime, 1000,
'animation.startTime should be at its effect end');
assert_times_equal(animation.currentTime, 1000,
'animation.currentTime should be at its effect end');
}, 'Reversing an idle animation from starts playing the animation');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
const scroller = animation.timeline.scrollSource;
// Make the scroll timeline inactive.
scroller.style.overflow = 'visible';
scroller.scrollTop;
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
assert_throws_dom('InvalidStateError', () => { animation.reverse(); });
}, 'Reversing an animation without an active timeline throws an ' +
'InvalidStateError');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = 500;
animation.pause();
await animation.ready;
animation.reverse();
await animation.ready;
assert_equals(animation.playState, 'running',
'Animation.playState should be "running" after reverse()');
}, 'Reversing an animation plays a pausing animation');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
await animation.ready;
animation.updatePlaybackRate(2);
animation.reverse();
await animation.ready;
assert_equals(animation.playbackRate, -2);
}, 'Reversing should use the negative pending playback rate');
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
animation.effect.updateTiming({ iterations: Infinity });
// Wait for new animation frame which allows the timeline to compute new
// current time.
await waitForNextFrame();
animation.play();
animation.currentTime = -200 * MS_PER_SEC;
await animation.ready;
animation.updatePlaybackRate(2);
assert_throws_dom('InvalidStateError', () => { animation.reverse(); });
assert_equals(animation.playbackRate, 1);
await animation.ready;
assert_equals(animation.playbackRate, 2);
}, 'When reversing fails, it should restore any previous pending playback'
+ ' rate');
</script>
</body>