Removed exception for setting playback rate on scroll linked animations
Initial hold time for scroll-linked animations now accounts for playback
rate.
Added test for setting playback rate on scroll linked animations
Bug: 916117
Change-Id: Ibd7e80ce60346d2f13155b13b8cc14252afc0c3a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1970911
Commit-Queue: Jordan Taylor <jortaylo@microsoft.com>
Reviewed-by: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#740882}
diff --git a/scroll-animations/scroll-animation.html b/scroll-animations/scroll-animation.html
index bee9e36..7a6f875 100644
--- a/scroll-animations/scroll-animation.html
+++ b/scroll-animations/scroll-animation.html
@@ -4,6 +4,7 @@
<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;
@@ -18,30 +19,6 @@
<div id="log"></div>
<script>
'use strict';
-
- function createScroller(test) {
- var scroller = createDiv(test);
- scroller.innerHTML = "<div class='contents'></div>";
- scroller.classList.add('scroller');
- return scroller;
- }
-
- function createScrollTimeline(test) {
- return new ScrollTimeline({
- scrollSource: createScroller(test),
- timeRange: 1000
- });
- }
-
- function createScrollLinkedAnimation(test, timeline) {
- if(timeline === undefined)
- timeline = createScrollTimeline(test);
- const DURATION = 1000; // ms
- const KEYFRAMES = { opacity: [1, 0] };
- return new Animation(
- new KeyframeEffect(createDiv(test), KEYFRAMES, DURATION), timeline);
- }
-
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
const scroller = animation.timeline.scrollSource;
diff --git a/scroll-animations/setting-current-time.html b/scroll-animations/setting-current-time.html
index dd2abb4..069a7cc 100644
--- a/scroll-animations/setting-current-time.html
+++ b/scroll-animations/setting-current-time.html
@@ -5,6 +5,7 @@
<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;
@@ -20,30 +21,6 @@
<div id="log"></div>
<script>
'use strict';
-
- function createScroller(test) {
- var scroller = createDiv(test);
- scroller.innerHTML = "<div class='contents'></div>";
- scroller.classList.add('scroller');
- return scroller;
- }
-
- function createScrollTimeline(test) {
- return new ScrollTimeline({
- scrollSource: createScroller(test),
- timeRange: 1000
- });
- }
-
- function createScrollLinkedAnimation(test, timeline) {
- if(timeline === undefined)
- timeline = createScrollTimeline(test);
- const DURATION = 1000; // ms
- const KEYFRAMES = { opacity: [1, 0] };
- return new Animation(
- new KeyframeEffect(createDiv(test), KEYFRAMES, DURATION), timeline);
- }
-
promise_test(async t => {
const animation = createScrollLinkedAnimation(t);
const scroller = animation.timeline.scrollSource;
diff --git a/scroll-animations/setting-playback-rate.html b/scroll-animations/setting-playback-rate.html
new file mode 100644
index 0000000..a5fdf09
--- /dev/null
+++ b/scroll-animations/setting-playback-rate.html
@@ -0,0 +1,253 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Setting the playback rate of an animation that is using a ScrollTimeline</title>
+<link rel="help" href="https://drafts.csswg.org/web-animations/#setting-the-playback-rate-of-an-animation">
+<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;
+}
+.contents {
+ height: 1000px;
+ width: 100%;
+}
+</style>
+<body>
+<script>
+'use strict';
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ // this forces a layout which results in an active timeline
+ scroller.scrollTop = 0;
+
+ animation.playbackRate = 0.5;
+ animation.play();
+
+ assert_equals(animation.currentTime, 0,
+ 'Zero current time is not affected by playbackRate change.');
+ }, 'Zero current time is not affected by playbackRate set while the ' +
+ 'animation is in idle state.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ // this forces a layout which results in an active timeline
+ scroller.scrollTop = 0;
+
+ animation.play();
+ animation.playbackRate = 0.5;
+
+ assert_equals(animation.currentTime, 0,
+ 'Zero current time is not affected by playbackRate change.');
+ }, 'Zero current time is not affected by playbackRate set while the ' +
+ 'animation is in play-pending state.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ const timeRange = animation.timeline.timeRange;
+ scroller.scrollTop = 0.2 * maxScroll;
+
+ animation.playbackRate = 0.5;
+ animation.play();
+ await animation.ready;
+ assert_equals(animation.currentTime, 0.2 * timeRange * 0.5,
+ 'Initial current time is scaled by playbackRate change.');
+ }, 'Initial current time is scaled by playbackRate set while ' +
+ 'scroll-linked animation is in running state.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ const timeRange = animation.timeline.timeRange;
+ const playbackRate = 2;
+
+ scroller.scrollTop = 0.2 * maxScroll;
+
+ animation.play();
+ await animation.ready;
+ // Set playback rate while the animation is playing.
+ animation.playbackRate = playbackRate;
+ assert_times_equal(animation.currentTime, 0.2 * timeRange,
+ 'The current time should stay unaffected by setting playback rate.');
+ }, 'The current time is not affected by playbackRate set while the ' +
+ 'scroll-linked animation is in play state.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ const timeRange = animation.timeline.timeRange;
+
+ // Set playback rate while the animation is in 'idle' state.
+ animation.playbackRate = 2;
+ animation.play();
+ await animation.ready;
+ scroller.scrollTop = 0.2 * maxScroll;
+
+ assert_times_equal(animation.currentTime, 0.2 * timeRange * 2,
+ 'The current time should increase two times faster than timeline time.');
+ }, 'The playback rate set before scroll-linked animation started playing ' +
+ 'affects the rate of progress of the current time');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ animation.play();
+
+ await animation.ready;
+
+ animation.playbackRate = 2;
+ scroller.scrollTop = 0.25 * maxScroll;
+
+ assert_times_equal(
+ animation.currentTime,
+ animation.timeline.currentTime * animation.playbackRate,
+ 'The current time should increase two times faster than timeline time'
+ );
+ }, 'The playback rate affects the rate of progress of the current time' +
+ ' when scrolling');
+
+ test(t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ scroller.scrollTop = 0.25 * maxScroll;
+ animation.play();
+
+ animation.playbackRate = 2;
+
+ assert_equals(animation.playState, "running");
+ assert_true(animation.pending);
+ assert_times_equal(animation.currentTime, animation.timeline.currentTime);
+ }, 'Setting the playback rate while play-pending preserves the current time' +
+ ' from scrollTimeline.');
+
+ test(t => {
+ const animation = createScrollLinkedAnimation(t);
+ animation.play();
+ animation.currentTime = 250;
+ animation.playbackRate = 2;
+
+ assert_equals(animation.playState, "running");
+ assert_true(animation.pending);
+ assert_times_equal(animation.currentTime, 250);
+ }, 'Setting the playback rate while play-pending preserves the set current' +
+ ' time.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ scroller.scrollTop = 0.25 * maxScroll;
+ animation.play();
+
+ await animation.ready;
+ animation.playbackRate = 2;
+
+ assert_times_equal(animation.currentTime, animation.timeline.currentTime);
+ }, 'Setting the playback rate while playing preserves the current time' +
+ ' from scrollTimeline.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+
+ /* Wait for animation frame is here for now to avoid a renderer crash
+ caused by crbug.com/1042924. Once that is fixed, these can be removed */
+ await waitForAnimationFrames(2);
+
+ animation.play();
+
+ animation.currentTime = 250;
+ await animation.ready;
+ animation.playbackRate = 2;
+
+ assert_times_equal(animation.currentTime, 250);
+ }, 'Setting the playback rate while playing preserves the set current time.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ const range = animation.timeline.timeRange;
+ animation.playbackRate = -1;
+ scroller.scrollTop = 0.3 * maxScroll;
+ animation.play();
+
+ await animation.ready;
+ const expectedCurrentTime = range - animation.timeline.currentTime;
+ assert_times_equal(animation.currentTime, expectedCurrentTime);
+ }, 'Negative initial playback rate should correctly modify initial current' +
+ ' time.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ scroller.scrollTop = 0.5 * maxScroll;
+ animation.play();
+
+ await animation.ready;
+ const startingTimelineTime = animation.timeline.currentTime;
+ const startingCurrentTime = animation.currentTime;
+ assert_times_equal(startingCurrentTime, startingTimelineTime);
+
+ animation.playbackRate = -1;
+
+ scroller.scrollTop = 0.8 * maxScroll;
+ // -300 = 500 - 800
+ let timelineDiff = startingTimelineTime - animation.timeline.currentTime;
+ // 200 = 500 + (-300)
+ let expected = startingCurrentTime + timelineDiff;
+ assert_times_equal(animation.currentTime, expected);
+
+ scroller.scrollTop = 0.2 * maxScroll;
+ // 300 = 500 - 200
+ timelineDiff = startingTimelineTime - animation.timeline.currentTime;
+ // 800 = 500 + 300
+ expected = startingCurrentTime + timelineDiff;
+ assert_times_equal(animation.currentTime, expected);
+ }, 'Reversing the playback rate while playing correctly impacts current' +
+ ' time during future scrolls');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ const range = animation.timeline.timeRange;
+ animation.playbackRate = 0;
+ scroller.scrollTop = 0.3 * maxScroll;
+ animation.play();
+
+ await animation.ready;
+ assert_times_equal(animation.currentTime, 0);
+ }, 'Zero initial playback rate should correctly modify initial current' +
+ ' time.');
+
+ promise_test(async t => {
+ const animation = createScrollLinkedAnimation(t);
+ const scroller = animation.timeline.scrollSource;
+ const maxScroll = scroller.scrollHeight - scroller.clientHeight;
+ scroller.scrollTop = 0.2 * maxScroll;
+ animation.play();
+
+ await animation.ready;
+ assert_times_equal(animation.currentTime, 200);
+ animation.playbackRate = 0;
+ scroller.scrollTop = 0.5 * maxScroll;
+
+ // Ensure that current time does not change.
+ assert_time_equals_literal(animation.timeline.currentTime, 500);
+ assert_time_equals_literal(animation.currentTime, 200);
+ }, 'Setting a zero playback rate while running preserves the current time');
+</script>
+</body>
diff --git a/scroll-animations/testcommon.js b/scroll-animations/testcommon.js
new file mode 100644
index 0000000..ca25968
--- /dev/null
+++ b/scroll-animations/testcommon.js
@@ -0,0 +1,22 @@
+function createScroller(test) {
+ var scroller = createDiv(test);
+ scroller.innerHTML = "<div class='contents'></div>";
+ scroller.classList.add('scroller');
+ return scroller;
+}
+
+function createScrollTimeline(test) {
+ return new ScrollTimeline({
+ scrollSource: createScroller(test),
+ timeRange: 1000
+ });
+}
+
+function createScrollLinkedAnimation(test, timeline) {
+ if(timeline === undefined)
+ timeline = createScrollTimeline(test);
+ const DURATION = 1000; // ms
+ const KEYFRAMES = { opacity: [1, 0] };
+ return new Animation(
+ new KeyframeEffect(createDiv(test), KEYFRAMES, DURATION), timeline);
+}