3 <title>ScrollTimeline current time algorithm
</title>
4 <link rel=
"help" href=
"https://wicg.github.io/scroll-animations/#current-time-algorithm">
5 <script src=
"/resources/testharness.js"></script>
6 <script src=
"/resources/testharnessreport.js"></script>
7 <script src=
"/web-animations/testcommon.js"></script>
8 <script src=
"./testcommon.js"></script>
15 promise_test(async t
=> {
16 const scroller
= setupScrollTimelineTest();
17 const scrollTimeline
= new ScrollTimeline({ scrollSource
: scroller
});
19 assert_equals(scrollTimeline
.duration
.unit
, "percent",
20 "duration returns as a percent for scroll timelines");
21 assert_equals(scrollTimeline
.duration
.value
, 100, "duration is 100%");
22 }, "Scroll timeline correctly returns duration as 100%");
24 promise_test(async t
=> {
25 const scroller
= setupScrollTimelineTest();
26 const scrollerSize
= scroller
.scrollHeight
- scroller
.clientHeight
;
28 const blockScrollTimeline
= new ScrollTimeline({
29 scrollSource
: scroller
,
32 const inlineScrollTimeline
= new ScrollTimeline({
33 scrollSource
: scroller
,
36 const horizontalScrollTimeline
= new ScrollTimeline({
37 scrollSource
: scroller
,
38 orientation
: 'horizontal'
40 const verticalScrollTimeline
= new ScrollTimeline({
41 scrollSource
: scroller
,
42 orientation
: 'vertical'
45 // Unscrolled, all timelines should read a currentTime of 0.
46 assert_percents_equal(blockScrollTimeline
.currentTime
, 0,
47 'Unscrolled block timeline');
48 assert_percents_equal(inlineScrollTimeline
.currentTime
, 0,
49 'Unscrolled inline timeline');
50 assert_percents_equal(horizontalScrollTimeline
.currentTime
, 0,
51 'Unscrolled horizontal timeline');
52 assert_percents_equal(verticalScrollTimeline
.currentTime
, 0,
53 'Unscrolled vertical timeline');
55 // Do some scrolling and make sure that the ScrollTimelines update.
56 scroller
.scrollTop
= 50;
57 scroller
.scrollLeft
= 75;
58 // Wait for new animation frame which allows the timeline to compute new
60 await
waitForNextFrame();
62 const inlineScrollRange
= scroller
.scrollWidth
- scroller
.clientWidth
;
63 const expectedInlineCurrentTime
=
64 100 * scroller
.scrollLeft
/ inlineScrollRange
;
66 const blockScrollRange
= scroller
.scrollHeight
- scroller
.clientHeight
;
67 const expectedBlockCurrentTime
=
68 100 * scroller
.scrollTop
/ blockScrollRange
;
70 assert_percents_approx_equal(blockScrollTimeline
.currentTime
,
71 expectedBlockCurrentTime
,
73 'Scrolled block timeline');
74 assert_percents_approx_equal(inlineScrollTimeline
.currentTime
,
75 expectedInlineCurrentTime
,
77 'Scrolled inline timeline');
78 assert_percents_approx_equal(horizontalScrollTimeline
.currentTime
,
79 expectedInlineCurrentTime
,
81 'Scrolled horizontal timeline');
82 assert_percents_approx_equal(verticalScrollTimeline
.currentTime
,
83 expectedBlockCurrentTime
,
85 'Scrolled vertical timeline');
86 }, 'currentTime calculates the correct time based on scroll progress');
89 promise_test(async t
=> {
90 const scroller
= setupScrollTimelineTest();
91 const scrollerSize
= scroller
.scrollHeight
- scroller
.clientHeight
;
93 const lengthScrollTimeline
= new ScrollTimeline({
94 scrollSource
: scroller
,
96 scrollOffsets
: [CSS
.px(20), 'auto']
98 const percentageScrollTimeline
= new ScrollTimeline({
99 scrollSource
: scroller
,
100 orientation
: 'block',
101 scrollOffsets
: [CSS
.percent(20), 'auto']
103 const calcScrollTimeline
= new ScrollTimeline({
104 scrollSource
: scroller
,
105 orientation
: 'block',
106 scrollOffsets
: [CSS
.percent(20).sub(CSS
.px(5)), 'auto']
109 // Unscrolled all timelines should read a current time of 0, as the
110 // current offset (0) will be less than the startScrollOffset.
111 assert_percents_equal(lengthScrollTimeline
.currentTime
, 0,
112 'Unscrolled length-based timeline current time');
113 assert_equals(lengthScrollTimeline
.phase
, "before",
114 'Unscrolled length-based timeline phase');
115 assert_percents_equal(percentageScrollTimeline
.currentTime
, 0,
116 'Unscrolled percentage-based timeline current time');
117 assert_equals(percentageScrollTimeline
.phase
, "before",
118 'Unscrolled percentage-based timeline phase');
119 assert_percents_equal(calcScrollTimeline
.currentTime
, 0,
120 'Unscrolled calc-based timeline current time');
121 assert_equals(calcScrollTimeline
.phase
, "before",
122 'Unscrolled calc-based timeline phase');
124 // Check the length-based ScrollTimeline.
125 scroller
.scrollTop
= 19;
126 // Wait for new animation frame which allows the timeline to compute new
128 await
waitForNextFrame();
129 assert_percents_equal(lengthScrollTimeline
.currentTime
, 0,
130 'Length-based timeline current time before the startScrollOffset point');
131 assert_equals(lengthScrollTimeline
.phase
, "before",
132 'Length-based timeline phase before the startScrollOffset point');
133 scroller
.scrollTop
= 20;
134 await
waitForNextFrame();
135 assert_percents_equal(lengthScrollTimeline
.currentTime
, 0,
136 'Length-based timeline current time at the startScrollOffset point');
137 assert_equals(lengthScrollTimeline
.phase
, "active",
138 'Length-based timeline phase at the startScrollOffset point');
139 scroller
.scrollTop
= 50;
140 await
waitForNextFrame();
141 assert_percents_equal(
142 lengthScrollTimeline
.currentTime
,
143 calculateCurrentTime(50, 20, scrollerSize
),
144 'Length-based timeline current time after the startScrollOffset point');
145 assert_equals(lengthScrollTimeline
.phase
, "active",
146 'Length-based timeline phase after the startScrollOffset point');
148 // Check the percentage-based ScrollTimeline.
149 scroller
.scrollTop
= 0.19 * scrollerSize
;
150 await
waitForNextFrame();
151 assert_percents_equal(percentageScrollTimeline
.currentTime
, 0,
152 'Percentage-based timeline current time before the startScrollOffset ' +
154 assert_equals(percentageScrollTimeline
.phase
, "before",
155 'Percentage-based timeline phase before the startScrollOffset point');
156 scroller
.scrollTop
= 0.20 * scrollerSize
;
157 await
waitForNextFrame();
158 assert_percents_equal(percentageScrollTimeline
.currentTime
, 0,
159 'Percentage-based timeline current time at the startScrollOffset point');
160 assert_equals(percentageScrollTimeline
.phase
, "active",
161 'Percentage-based timeline phase at the startScrollOffset point');
162 scroller
.scrollTop
= 0.50 * scrollerSize
;
163 await
waitForNextFrame();
164 assert_percents_equal(
165 percentageScrollTimeline
.currentTime
,
166 calculateCurrentTime(
167 scroller
.scrollTop
, 0.2 * scrollerSize
, scrollerSize
),
168 'Percentage-based timeline current time after the startScrollOffset ' +
170 assert_equals(percentageScrollTimeline
.phase
, "active",
171 'Percentage-based timeline phase after the startScrollOffset point');
173 // Check the calc-based ScrollTimeline.
174 scroller
.scrollTop
= 0.2 * scrollerSize
- 10;
175 await
waitForNextFrame();
176 assert_percents_equal(calcScrollTimeline
.currentTime
, 0,
177 'Calc-based timeline current time before the startScrollOffset point');
178 assert_equals(calcScrollTimeline
.phase
, "before",
179 'Calc-based timeline phase at the startScrollOffset point');
180 scroller
.scrollTop
= 0.2 * scrollerSize
- 5;
181 await
waitForNextFrame();
182 assert_percents_equal(calcScrollTimeline
.currentTime
, 0,
183 'Calc-based timeline current time at the startScrollOffset point');
184 assert_equals(calcScrollTimeline
.phase
, "active",
185 'Calc-based timeline phase at the startScrollOffset point');
186 scroller
.scrollTop
= 0.2 * scrollerSize
;
187 await
waitForNextFrame();
188 assert_percents_equal(
189 calcScrollTimeline
.currentTime
,
190 calculateCurrentTime(
191 scroller
.scrollTop
, 0.2 * scrollerSize
- 5, scrollerSize
),
192 'Calc-based timeline current time after the startScrollOffset point');
193 assert_equals(calcScrollTimeline
.phase
, "active",
194 'Calc-based timeline phase at the startScrollOffset point');
195 }, 'currentTime handles startScrollOffset correctly');
197 promise_test(async t
=> {
198 const scroller
= setupScrollTimelineTest();
199 const scrollerSize
= scroller
.scrollHeight
- scroller
.clientHeight
;
201 // When the endScrollOffset is equal to the maximum scroll offset (and there
202 // are no fill modes), the endScrollOffset is treated as inclusive.
203 const inclusiveAutoScrollTimeline
= new ScrollTimeline({
204 scrollSource
: scroller
,
205 orientation
: 'block',
207 const inclusiveLengthScrollTimeline
= new ScrollTimeline({
208 scrollSource
: scroller
,
209 orientation
: 'block',
210 scrollOffsets
: [CSS
.px(scrollerSize
)]
212 const inclusivePercentageScrollTimeline
= new ScrollTimeline({
213 scrollSource
: scroller
,
214 orientation
: 'block',
215 scrollOffsets
: [CSS
.percent(100)]
217 const inclusiveCalcScrollTimeline
= new ScrollTimeline({
218 scrollSource
: scroller
,
219 orientation
: 'block',
220 scrollOffsets
: [CSS
.percent(80).add(CSS
.px(0.2 * scrollerSize
))]
223 scroller
.scrollTop
= scrollerSize
;
224 let expectedCurrentTime
= calculateCurrentTime(
225 scroller
.scrollTop
, 0, scrollerSize
);
227 // Wait for new animation frame which allows the timeline to compute new
229 await
waitForNextFrame();
231 assert_percents_equal(
232 inclusiveAutoScrollTimeline
.currentTime
, expectedCurrentTime
,
233 'Inclusive auto timeline at the endScrollOffset point');
234 assert_percents_equal(
235 inclusiveLengthScrollTimeline
.currentTime
, expectedCurrentTime
,
236 'Inclusive length-based timeline at the endScrollOffset point');
237 assert_percents_equal(
238 inclusivePercentageScrollTimeline
.currentTime
, expectedCurrentTime
,
239 'Inclusive percentage-based timeline at the endScrollOffset point');
240 assert_percents_equal(
241 inclusiveCalcScrollTimeline
.currentTime
, expectedCurrentTime
,
242 'Inclusive calc-based timeline at the endScrollOffset point');
243 }, 'currentTime handles endScrollOffset correctly (inclusive cases)');
245 promise_test(async t
=> {
246 const scroller
= setupScrollTimelineTest();
247 const scrollerSize
= scroller
.scrollHeight
- scroller
.clientHeight
;
249 const lengthScrollTimeline
= new ScrollTimeline({
250 scrollSource
: scroller
,
251 orientation
: 'block',
252 scrollOffsets
: [CSS
.px(scrollerSize
- 20)]
254 const percentageScrollTimeline
= new ScrollTimeline({
255 scrollSource
: scroller
,
256 orientation
: 'block',
257 scrollOffsets
: [CSS
.percent(80)]
259 const calcScrollTimeline
= new ScrollTimeline({
260 scrollSource
: scroller
,
261 orientation
: 'block',
262 scrollOffsets
: [CSS
.percent(80).add(CSS
.px(5))]
265 // Check the length-based ScrollTimeline.
266 scroller
.scrollTop
= scrollerSize
;
267 // Wait for new animation frame which allows the timeline to compute new
269 await
waitForNextFrame();
270 assert_percents_equal(lengthScrollTimeline
.currentTime
, 100,
271 'Length-based timeline current time after the endScrollOffset point');
272 assert_equals(lengthScrollTimeline
.phase
, "after",
273 'Length-based timeline phase after the endScrollOffset point');
274 scroller
.scrollTop
= scrollerSize
- 20;
275 await
waitForNextFrame();
276 assert_percents_equal(lengthScrollTimeline
.currentTime
, 100,
277 'Length-based timeline current time at the endScrollOffset point');
278 assert_equals(lengthScrollTimeline
.phase
, "after",
279 'Length-based timeline phase at the endScrollOffset point');
280 scroller
.scrollTop
= scrollerSize
- 50;
281 await
waitForNextFrame();
282 assert_percents_equal(
283 lengthScrollTimeline
.currentTime
,
284 calculateCurrentTime(scrollerSize
- 50, 0, scrollerSize
- 20),
285 'Length-based timeline current time before the endScrollOffset point');
286 assert_equals(lengthScrollTimeline
.phase
, "active",
287 'Length-based timeline phase before the endScrollOffset point');
289 // Check the percentage-based ScrollTimeline.
290 scroller
.scrollTop
= 0.81 * scrollerSize
;
291 await
waitForNextFrame();
292 assert_percents_equal(percentageScrollTimeline
.currentTime
, 100,
293 'Percentage-based timeline current time after the endScrollOffset point');
294 assert_equals(percentageScrollTimeline
.phase
, "after",
295 'Percentage-based timeline phase after the endScrollOffset point');
296 scroller
.scrollTop
= 0.80 * scrollerSize
;
297 await
waitForNextFrame();
298 assert_percents_equal(percentageScrollTimeline
.currentTime
, 100,
299 'Percentage-based timeline current time at the endScrollOffset point');
300 assert_equals(percentageScrollTimeline
.phase
, "after",
301 'Percentage-based timeline phase at the endScrollOffset point');
302 scroller
.scrollTop
= 0.50 * scrollerSize
;
303 await
waitForNextFrame();
304 assert_percents_equal(
305 percentageScrollTimeline
.currentTime
,
306 calculateCurrentTime(scroller
.scrollTop
, 0, 0.8 * scrollerSize
),
307 'Percentage-based timeline current time before the endScrollOffset ' +
309 assert_equals(percentageScrollTimeline
.phase
, "active",
310 'Percentage-based timeline phase before the endScrollOffset point');
312 // Check the calc-based ScrollTimeline.
313 scroller
.scrollTop
= 0.8 * scrollerSize
+ 6;
314 await
waitForNextFrame();
315 assert_percents_equal(calcScrollTimeline
.currentTime
, 100,
316 'Calc-based timeline current time after the endScrollOffset point');
317 assert_equals(calcScrollTimeline
.phase
, "after",
318 'Calc-based timeline phase after the endScrollOffset point');
319 scroller
.scrollTop
= 0.8 * scrollerSize
+ 5;
320 await
waitForNextFrame();
321 assert_percents_equal(calcScrollTimeline
.currentTime
, 100,
322 'Calc-based timeline current time at the endScrollOffset point');
323 assert_equals(calcScrollTimeline
.phase
, "after",
324 'Calc-based timeline phase at the endScrollOffset point');
325 scroller
.scrollTop
= 0.5 * scrollerSize
;
326 await
waitForNextFrame();
327 assert_percents_equal(
328 calcScrollTimeline
.currentTime
,
329 calculateCurrentTime(scroller
.scrollTop
, 0, 0.8 * scrollerSize
+ 5),
330 'Calc-based timeline current time before the endScrollOffset point');
331 assert_equals(calcScrollTimeline
.phase
, "active",
332 'Calc-based timeline phase before the endScrollOffset point');
333 }, 'currentTime handles endScrollOffset correctly');
335 promise_test(async t
=> {
336 const scroller
= setupScrollTimelineTest();
337 const scrollerSize
= scroller
.scrollHeight
- scroller
.clientHeight
;
339 const scrollTimeline
= new ScrollTimeline({
340 scrollSource
: scroller
,
341 orientation
: 'block',
342 scrollOffsets
: [CSS
.px(20), CSS
.px(scrollerSize
- 50)]
345 scroller
.scrollTop
= 150;
346 // Wait for new animation frame which allows the timeline to compute new
348 await
waitForNextFrame();
349 assert_percents_equal(
350 scrollTimeline
.currentTime
,
351 calculateCurrentTime(150, 20, scrollerSize
- 50));
352 }, 'currentTime handles startScrollOffset and endScrollOffset together' +
355 promise_test(async t
=> {
356 const scroller
= setupScrollTimelineTest();
357 const scrollTimeline
= new ScrollTimeline({
358 scrollSource
: scroller
,
359 orientation
: 'block',
360 scrollOffsets
: [CSS
.px(20), CSS
.px(20)]
363 scroller
.scrollTop
= 150;
364 // Wait for new animation frame which allows the timeline to compute new
366 await
waitForNextFrame();
367 assert_percents_equal(scrollTimeline
.currentTime
, 100);
368 }, 'currentTime handles startScrollOffset == endScrollOffset correctly');
370 promise_test(async t
=> {
371 const scroller
= setupScrollTimelineTest();
372 const scrollTimeline
= new ScrollTimeline({
373 scrollSource
: scroller
,
374 orientation
: 'block',
375 scrollOffsets
: [CSS
.px(50), CSS
.px(10)]
378 scroller
.scrollTop
= 40;
379 // Wait for new animation frame which allows the timeline to compute new
381 await
waitForNextFrame();
382 assert_percents_equal(scrollTimeline
.currentTime
, 0);
383 scroller
.scrollTop
= 60;
384 await
waitForNextFrame();
385 assert_percents_equal(scrollTimeline
.currentTime
, 100);
386 }, 'currentTime handles startScrollOffset > endScrollOffset correctly');
388 promise_test(async t
=> {
389 const scroller
= setupScrollTimelineTest();
390 const scrollTimeline
= new ScrollTimeline({
391 scrollSource
: scroller
,
392 orientation
: 'block',
393 scrollOffsets
: [CSS
.px(10), CSS
.px(20), CSS
.px(40), CSS
.px(70), CSS
.px(90)],
397 var w
= 1 / 4; // offset weight
398 var p
= 0; // progress within the offset
400 scroller
.scrollTop
= 10;
401 // Wait for new animation frame which allows the timeline to compute new
403 await
waitForNextFrame();
405 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
406 "current time calculation when scroll = " + scroller
.scrollTop
);
408 p
= (12 - 10) / (20 - 10);
409 scroller
.scrollTop
= 12;
410 await
waitForNextFrame();
411 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
412 "current time calculation when scroll = " + scroller
.scrollTop
);
416 scroller
.scrollTop
= 20;
417 await
waitForNextFrame();
418 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
419 "current time calculation when scroll = " + scroller
.scrollTop
);
421 p
= (35 - 20) / (40 - 20);
422 scroller
.scrollTop
= 35;
423 await
waitForNextFrame();
424 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
425 "current time calculation when scroll = " + scroller
.scrollTop
);
429 scroller
.scrollTop
= 40;
430 await
waitForNextFrame();
431 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
432 "current time calculation when scroll = " + scroller
.scrollTop
);
434 p
= (60 - 40) / (70 - 40);
435 scroller
.scrollTop
= 60;
436 await
waitForNextFrame();
437 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
438 "current time calculation when scroll = " + scroller
.scrollTop
);
442 scroller
.scrollTop
= 70;
443 await
waitForNextFrame();
444 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
445 "current time calculation when scroll = " + scroller
.scrollTop
);
447 p
= (80 - 70) / (90 - 70);
448 scroller
.scrollTop
= 80;
449 await
waitForNextFrame();
450 assert_percents_equal(scrollTimeline
.currentTime
, (offset
+ p
) * w
* 100,
451 "current time calculation when scroll = " + scroller
.scrollTop
);
453 scroller
.scrollTop
= 90;
454 await
waitForNextFrame();
455 assert_percents_equal(scrollTimeline
.currentTime
, 100,
456 "current time calculation when scroll = " + scroller
.scrollTop
);
457 }, 'currentTime calculations when multiple scroll offsets are specified');