| <!DOCTYPE html> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule"> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-offset-section"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/web-animations/testcommon.js"></script> |
| <style> |
| #scroller { |
| overflow: scroll; |
| width: 100px; |
| height: 100px; |
| } |
| #contents { |
| height: 300px; |
| } |
| @keyframes expand { |
| from { width: 100px; } |
| to { width: 200px; } |
| } |
| @scroll-timeline timeline_0px_100px { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: 0px; |
| end: 100px; |
| } |
| @scroll-timeline timeline_50px_100px { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: 50px; |
| end: 100px; |
| } |
| @scroll-timeline timeline_auto_auto { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: auto; |
| end: auto; |
| } |
| @scroll-timeline timeline_auto_auto_implicit { |
| source: selector(#scroller); |
| time-range: 10s; |
| } |
| @scroll-timeline timeline_50px_auto { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: 50px; |
| end: auto; |
| } |
| @scroll-timeline timeline_auto_100px { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: auto; |
| end: 100px; |
| } |
| @scroll-timeline timeline_25p_75p { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: 25%; |
| end: 75%; |
| } |
| @scroll-timeline timeline_calc_calc { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: calc(25% + 10px); |
| end: calc(75% + 10px); |
| } |
| #container > div { |
| width: 0px; |
| animation-name: expand; |
| animation-duration: 10s; |
| animation-timing-function: linear; |
| } |
| /* Ensure stable expectations if feature is not supported */ |
| @supports not (animation-timeline:foo) { |
| #container > div { animation-play-state: paused; } |
| } |
| #element_0px_100px { animation-timeline: timeline_0px_100px; } |
| #element_50px_100px { animation-timeline: timeline_50px_100px; } |
| #element_auto_auto { animation-timeline: timeline_auto_auto; } |
| #element_auto_auto_implicit { animation-timeline: timeline_auto_auto_implicit; } |
| #element_50px_auto { animation-timeline: timeline_50px_auto; } |
| #element_auto_100px { animation-timeline: timeline_auto_100px; } |
| #element_25p_75p { animation-timeline: timeline_25p_75p; } |
| #element_calc_calc { animation-timeline: timeline_calc_calc; } |
| </style> |
| <div id=scroller> |
| <div id=contents></div> |
| </div> |
| <div id=container> |
| <div id=element_0px_100px></div> |
| <div id=element_50px_100px></div> |
| <div id=element_auto_auto></div> |
| <div id=element_auto_auto_implicit></div> |
| <div id=element_50px_auto></div> |
| <div id=element_auto_100px></div> |
| <div id=element_25p_75p></div> |
| <div id=element_calc_calc></div> |
| </div> |
| <script> |
| |
| // Scrolls top to 'offset', waits for a frame, then call the provided |
| // assertions function. |
| function test_scroll(element, offset, assertions, description) { |
| promise_test(async (t) => { |
| scroller.scrollTop = offset; |
| await waitForNextFrame(); |
| assertions(); |
| }, `${description} [${element.id}]`); |
| } |
| |
| // Tests that the computed value of 'width' on element is the expected value |
| // after scrolling top to the specifed offset. |
| function test_width_at_scroll_top(element, offset, expected) { |
| test_scroll(element, offset, () => { |
| assert_equals(getComputedStyle(element).width, expected); |
| }, `Scroll at offset ${offset} updates animation correctly`); |
| } |
| |
| // Tests that the computed value of 'width' on element is (approximately) |
| // the expected value after scrolling top to the offset specified by |
| // 'fraction'. The 'fraction' parameter is either a number which maps [0, 1] |
| // to the full scroll range, or a function which accepts the maximum scroll, |
| // and returns a specific offset. |
| function test_approximate_width_at_fraction(element, fraction, expected) { |
| const max = scroller.scrollHeight - scroller.clientHeight; |
| const offsetFunction = (typeof fraction == 'function') ? fraction : () => max * fraction; |
| const resolvedOffset = Math.floor(offsetFunction(max)); |
| test_scroll(element, resolvedOffset, () => { |
| assert_approx_equals(parseInt(getComputedStyle(element).width), parseInt(expected), 5); |
| }, `Scroll at offset ${resolvedOffset} updates animation correctly`); |
| } |
| |
| // [0px, 100px] |
| test_width_at_scroll_top(element_0px_100px, 0, '100px'); |
| test_width_at_scroll_top(element_0px_100px, 1, '101px'); |
| test_width_at_scroll_top(element_0px_100px, 50, '150px'); |
| test_width_at_scroll_top(element_0px_100px, 99, '199px'); |
| test_width_at_scroll_top(element_0px_100px, 100, '0px'); |
| test_width_at_scroll_top(element_0px_100px, 101, '0px'); |
| |
| // [50px, 100px] |
| test_width_at_scroll_top(element_50px_100px, 0, '0px'); |
| test_width_at_scroll_top(element_50px_100px, 1, '0px'); |
| test_width_at_scroll_top(element_50px_100px, 49, '0px'); |
| test_width_at_scroll_top(element_50px_100px, 50, '100px'); |
| test_width_at_scroll_top(element_50px_100px, 51, '102px'); |
| test_width_at_scroll_top(element_50px_100px, 99, '198px'); |
| test_width_at_scroll_top(element_50px_100px, 100, '0px'); |
| test_width_at_scroll_top(element_50px_100px, 101, '0px'); |
| |
| // [auto, auto] |
| test_approximate_width_at_fraction(element_auto_auto, 0, '100px'); |
| test_approximate_width_at_fraction(element_auto_auto, 0.1, '110px'); |
| test_approximate_width_at_fraction(element_auto_auto, 0.5, '150px'); |
| test_approximate_width_at_fraction(element_auto_auto, 0.9, '190px'); |
| // TODO: Effects for scroll linked animations are not inclusive at max scroll |
| // https://github.com/w3c/csswg-drafts/issues/5223 |
| test_approximate_width_at_fraction(element_auto_auto, 1, '0px'); |
| |
| // [auto, auto] (implicit) |
| test_approximate_width_at_fraction(element_auto_auto_implicit, 0, '100px'); |
| test_approximate_width_at_fraction(element_auto_auto_implicit, 0.1, '110px'); |
| test_approximate_width_at_fraction(element_auto_auto_implicit, 0.5, '150px'); |
| test_approximate_width_at_fraction(element_auto_auto_implicit, 0.9, '190px'); |
| // TODO: Effects for scroll linked animations are not inclusive at max scroll |
| // https://github.com/w3c/csswg-drafts/issues/5223 |
| test_approximate_width_at_fraction(element_auto_auto_implicit, 1, '0px'); |
| |
| // [50px, auto] |
| { |
| let offset = (t) => (max => 50 * (1 - t) + max * t); |
| test_width_at_scroll_top(element_50px_auto, 0, '0px'); |
| test_width_at_scroll_top(element_50px_auto, 49, '0px'); |
| test_width_at_scroll_top(element_50px_auto, 50, '100px'); |
| test_approximate_width_at_fraction(element_50px_auto, offset(0.5), '150px'); |
| test_approximate_width_at_fraction(element_50px_auto, offset(0.9), '190px'); |
| test_approximate_width_at_fraction(element_50px_auto, 1, '0px'); |
| } |
| |
| // [auto, 100px] |
| test_width_at_scroll_top(element_auto_100px, 0, '100px'); |
| test_width_at_scroll_top(element_auto_100px, 1, '101px'); |
| test_width_at_scroll_top(element_auto_100px, 50, '150px'); |
| test_width_at_scroll_top(element_auto_100px, 99, '199px'); |
| test_width_at_scroll_top(element_auto_100px, 100, '0px'); |
| |
| // [25%, 75%] |
| test_approximate_width_at_fraction(element_25p_75p, 0, '0px'); |
| test_approximate_width_at_fraction(element_25p_75p, 0.1, '0px'); |
| test_approximate_width_at_fraction(element_25p_75p, 0.2, '0px'); |
| test_approximate_width_at_fraction(element_25p_75p, 0.35, '120px'); |
| test_approximate_width_at_fraction(element_25p_75p, 0.55, '160px'); |
| test_approximate_width_at_fraction(element_25p_75p, 0.8, '0px'); |
| test_approximate_width_at_fraction(element_25p_75p, 1, '0px'); |
| |
| // [calc(25% + 10px), calc(75% + 10px)] |
| { |
| let offset = (t) => (max => (max * 0.25 + 10) * (1 - t) + (max * 0.75 + 10) * t); |
| test_approximate_width_at_fraction(element_calc_calc, offset(0), '0px'); |
| test_approximate_width_at_fraction(element_calc_calc, offset(0.5), '150px'); |
| test_approximate_width_at_fraction(element_calc_calc, offset(1.1), '0px'); |
| } |
| |
| </script> |