| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>ScrollTimeline current time algorithm - interaction with writing modes</title> |
| <link rel="help" href="https://wicg.github.io/scroll-animations/#current-time-algorithm"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/web-animations/testcommon.js"></script> |
| <script src="./testcommon.js"></script> |
| |
| <body></body> |
| |
| <script> |
| 'use strict'; |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['direction', 'rtl']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const verticalScrollRange = scroller.scrollHeight - scroller.clientHeight; |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| const blockScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'block'}); |
| const inlineScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'inline'}); |
| const horizontalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'horizontal'}); |
| const verticalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'vertical'}); |
| |
| // Unscrolled, all timelines should read a current time of 0 even though the |
| // X-axis will have started at the right hand side for rtl. |
| assert_percents_equal(blockScrollTimeline.currentTime, 0, |
| 'Unscrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 0, |
| 'Unscrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 0, |
| 'Unscrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 0, |
| 'Unscrolled vertical timeline'); |
| |
| // The offset in the inline/horizontal direction should be inverted. The |
| // block/vertical direction should be unaffected. |
| scroller.scrollTop = 0.1 * verticalScrollRange; |
| scroller.scrollLeft = -0.8 * horizontalScrollRange; |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| |
| assert_percents_equal(blockScrollTimeline.currentTime, 10, |
| 'Scrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 80, |
| 'Scrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 80, |
| 'Scrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 10, |
| 'Scrolled vertical timeline'); |
| }, 'currentTime handles direction: rtl correctly'); |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['writing-mode', 'vertical-rl']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const verticalScrollRange = scroller.scrollHeight - scroller.clientHeight; |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| const blockScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'block'}); |
| const inlineScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'inline'}); |
| const horizontalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'horizontal'}); |
| const verticalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'vertical'}); |
| |
| // Unscrolled, all timelines should read a current time of 0 even though the |
| // X-axis will have started at the right hand side for vertical-rl. |
| assert_percents_equal(blockScrollTimeline.currentTime, 0, |
| 'Unscrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 0, |
| 'Unscrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 0, |
| 'Unscrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 0, |
| 'Unscrolled vertical timeline'); |
| |
| // For vertical-rl, the X-axis starts on the right-hand-side and is the block |
| // axis. The Y-axis is normal but is the inline axis. For the |
| // horizontal/vertical cases, horizontal starts on the right-hand-side and |
| // vertical is normal. |
| scroller.scrollTop = 0.1 * verticalScrollRange; |
| scroller.scrollLeft = -0.8 * horizontalScrollRange; |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| |
| assert_percents_equal(blockScrollTimeline.currentTime, 80, |
| 'Scrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 10, |
| 'Scrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 80, |
| 'Scrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 10, |
| 'Scrolled vertical timeline'); |
| }, 'currentTime handles writing-mode: vertical-rl correctly'); |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['writing-mode', 'vertical-lr']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const verticalScrollRange = scroller.scrollHeight - scroller.clientHeight; |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| const blockScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'block'}); |
| const inlineScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'inline'}); |
| const horizontalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'horizontal'}); |
| const verticalScrollTimeline = new ScrollTimeline( |
| {source: scroller, orientation: 'vertical'}); |
| |
| // Unscrolled, all timelines should read a current time of 0. |
| assert_percents_equal(blockScrollTimeline.currentTime, 0, |
| 'Unscrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 0, |
| 'Unscrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 0, |
| 'Unscrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 0, |
| 'Unscrolled vertical timeline'); |
| |
| // For vertical-lr, both axes start at their 'normal' positions but the X-axis |
| // is the block direction and the Y-axis is the inline direction. This does |
| // not affect horizontal/vertical. |
| scroller.scrollTop = 0.1 * verticalScrollRange; |
| scroller.scrollLeft = 0.2 * horizontalScrollRange; |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| |
| assert_percents_equal(blockScrollTimeline.currentTime, 20, |
| 'Scrolled block timeline'); |
| assert_percents_equal(inlineScrollTimeline.currentTime, 10, |
| 'Scrolled inline timeline'); |
| assert_percents_equal(horizontalScrollTimeline.currentTime, 20, |
| 'Scrolled horizontal timeline'); |
| assert_percents_equal(verticalScrollTimeline.currentTime, 10, |
| 'Scrolled vertical timeline'); |
| }, 'currentTime handles writing-mode: vertical-lr correctly'); |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['direction', 'rtl']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| const lengthScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.px(20), 'auto'] |
| }); |
| const percentageScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.percent(20), 'auto'] |
| }); |
| const calcScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.percent(20).sub(CSS.px(5)), 'auto'] |
| }); |
| |
| // Unscrolled, all timelines should read a current time of 0, since |
| // the current offset (0) will be less than the startScrollOffset. |
| assert_percents_equal(lengthScrollTimeline.currentTime, 0, |
| 'Unscrolled length-based timeline'); |
| assert_percents_equal(percentageScrollTimeline.currentTime, 0, |
| 'Unscrolled percentage-based timeline'); |
| assert_percents_equal(calcScrollTimeline.currentTime, 0, |
| 'Unscrolled calc-based timeline'); |
| |
| // With direction rtl offsets are inverted, such that scrollLeft == 0 |
| // is the 'zero' point for currentTime. However the |
| // startScrollOffset is an absolute distance along the offset, so doesn't |
| // need adjusting. |
| |
| // Check the length-based ScrollTimeline. |
| scroller.scrollLeft = 0; |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| assert_percents_equal(lengthScrollTimeline.currentTime, 0, |
| 'Length-based timeline before the startScrollOffset point'); |
| scroller.scrollLeft = -20; |
| await waitForNextFrame(); |
| assert_percents_equal(lengthScrollTimeline.currentTime, 0, |
| 'Length-based timeline at the startScrollOffset point'); |
| scroller.scrollLeft = -50; |
| await waitForNextFrame(); |
| assert_percents_equal( |
| lengthScrollTimeline.currentTime, |
| calculateCurrentTime(50, 20, horizontalScrollRange), |
| 'Length-based timeline after the startScrollOffset point'); |
| |
| // Check the percentage-based ScrollTimeline. |
| scroller.scrollLeft = -(0.19 * horizontalScrollRange); |
| await waitForNextFrame(); |
| assert_percents_equal(percentageScrollTimeline.currentTime, 0, |
| 'Percentage-based timeline before the startScrollOffset point'); |
| scroller.scrollLeft = -(0.20 * horizontalScrollRange); |
| await waitForNextFrame(); |
| assert_percents_equal(percentageScrollTimeline.currentTime, 0, |
| 'Percentage-based timeline at the startScrollOffset point'); |
| scroller.scrollLeft = -(0.4 * horizontalScrollRange); |
| await waitForNextFrame(); |
| assert_percents_equal( |
| percentageScrollTimeline.currentTime, |
| calculateCurrentTime( |
| 0.4 * horizontalScrollRange, 0.2 * horizontalScrollRange, |
| horizontalScrollRange), |
| 'Percentage-based timeline after the startScrollOffset point'); |
| |
| // Check the calc-based ScrollTimeline. |
| scroller.scrollLeft = -(0.2 * horizontalScrollRange - 10); |
| await waitForNextFrame(); |
| assert_percents_equal(calcScrollTimeline.currentTime, 0, |
| 'Calc-based timeline before the startScrollOffset point'); |
| scroller.scrollLeft = -(0.2 * horizontalScrollRange - 5); |
| await waitForNextFrame(); |
| assert_percents_equal(calcScrollTimeline.currentTime, 0, |
| 'Calc-based timeline at the startScrollOffset point'); |
| scroller.scrollLeft = -(0.2 * horizontalScrollRange); |
| await waitForNextFrame(); |
| assert_percents_equal( |
| calcScrollTimeline.currentTime, |
| calculateCurrentTime( |
| 0.2 * horizontalScrollRange, 0.2 * horizontalScrollRange - 5, |
| horizontalScrollRange), |
| 'Calc-based timeline after the startScrollOffset point'); |
| }, 'currentTime handles startScrollOffset with direction: rtl correctly'); |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['direction', 'rtl']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| const lengthScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.px(horizontalScrollRange - 20)] |
| }); |
| const percentageScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.percent(80)] |
| }); |
| const calcScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'horizontal', |
| scrollOffsets: [CSS.percent(80).add(CSS.px(5))] |
| }); |
| |
| // With direction rtl offsets are inverted, such that scrollLeft == 0 |
| // is the 'zero' point for currentTime. However the |
| // endScrollOffset is an absolute distance along the offset, so doesn't need |
| // adjusting. |
| |
| // Check the length-based ScrollTimeline. |
| scroller.scrollLeft = -horizontalScrollRange; |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| assert_percents_equal(lengthScrollTimeline.currentTime, 100, |
| 'Length-based timeline after the endScrollOffset point'); |
| scroller.scrollLeft = 20 - horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal(lengthScrollTimeline.currentTime, 100, |
| 'Length-based timeline at the endScrollOffset point'); |
| scroller.scrollLeft = 50 - horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal( |
| lengthScrollTimeline.currentTime, |
| calculateCurrentTime( |
| horizontalScrollRange - 50, 0, horizontalScrollRange - 20), |
| 'Length-based timeline before the endScrollOffset point'); |
| |
| // Check the percentage-based ScrollTimeline. |
| scroller.scrollLeft = 0.19 * horizontalScrollRange - horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal(percentageScrollTimeline.currentTime, 100, |
| 'Percentage-based timeline after the endScrollOffset point'); |
| scroller.scrollLeft = 0.20 * horizontalScrollRange - horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal(percentageScrollTimeline.currentTime, 100, |
| 'Percentage-based timeline at the endScrollOffset point'); |
| scroller.scrollLeft = 0.4 * horizontalScrollRange - horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal( |
| percentageScrollTimeline.currentTime, |
| calculateCurrentTime( |
| 0.6 * horizontalScrollRange, 0, 0.8 * horizontalScrollRange), |
| 'Percentage-based timeline before the endScrollOffset point'); |
| |
| // Check the calc-based ScrollTimeline. 80% + 5px |
| scroller.scrollLeft = -0.8 * horizontalScrollRange - 10; |
| await waitForNextFrame(); |
| assert_percents_equal(calcScrollTimeline.currentTime, 100, |
| 'Calc-based timeline after the endScrollOffset point'); |
| scroller.scrollLeft = -0.8 * horizontalScrollRange - 5; |
| await waitForNextFrame(); |
| assert_percents_equal(calcScrollTimeline.currentTime, 100, |
| 'Calc-based timeline at the endScrollOffset point'); |
| scroller.scrollLeft = -0.8 * horizontalScrollRange; |
| await waitForNextFrame(); |
| assert_percents_equal( |
| calcScrollTimeline.currentTime, |
| calculateCurrentTime( |
| 0.8 * horizontalScrollRange, 0, 0.8 * horizontalScrollRange + 5), |
| 'Calc-based timeline before the endScrollOffset point'); |
| }, 'currentTime handles endScrollOffset with direction: rtl correctly'); |
| |
| promise_test(async t => { |
| const scrollerOverrides = new Map([['direction', 'rtl']]); |
| const scroller = setupScrollTimelineTest(scrollerOverrides); |
| const horizontalScrollRange = scroller.scrollWidth - scroller.clientWidth; |
| |
| // When the endScrollOffset is equal to the maximum scroll offset (and there |
| // are no fill modes), the endScrollOffset is treated as inclusive. |
| const inclusiveAutoScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'inline', |
| }); |
| const inclusiveLengthScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'inline', |
| scrollOffsets: [CSS.px(horizontalScrollRange)] |
| }); |
| const inclusivePercentageScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'inline', |
| scrollOffsets: [CSS.percent(100)] |
| }); |
| const inclusiveCalcScrollTimeline = new ScrollTimeline({ |
| source: scroller, |
| orientation: 'inline', |
| scrollOffsets: [CSS.percent(80).sub(CSS.px(0.2 * horizontalScrollRange))] |
| }); |
| |
| // With direction rtl offsets are inverted, such that scrollLeft == |
| // horizontalScrollRange is the 'zero' point for currentTime. However the |
| // endScrollOffset is an absolute distance along the offset, so doesn't need |
| // adjusting. |
| |
| scroller.scrollLeft = 0; |
| let expectedCurrentTime = calculateCurrentTime( |
| scroller.scrollLeft, 0, horizontalScrollRange); |
| // Wait for new animation frame which allows the timeline to compute new |
| // current time. |
| await waitForNextFrame(); |
| |
| assert_percents_equal( |
| inclusiveAutoScrollTimeline.currentTime, expectedCurrentTime, |
| 'Inclusive auto timeline at the endScrollOffset point'); |
| assert_percents_equal( |
| inclusiveLengthScrollTimeline.currentTime, expectedCurrentTime, |
| 'Inclusive length-based timeline at the endScrollOffset point'); |
| assert_percents_equal( |
| inclusivePercentageScrollTimeline.currentTime, expectedCurrentTime, |
| 'Inclusive percentage-based timeline at the endScrollOffset point'); |
| assert_percents_equal( |
| inclusiveCalcScrollTimeline.currentTime, expectedCurrentTime, |
| 'Inclusive calc-based timeline at the endScrollOffset point'); |
| }, 'currentTime handles endScrollOffset (inclusive case) with direction: rtl' + |
| ' correctly'); |
| </script> |