| <!DOCTYPE html> |
| <title>@scroll-timeline: Element-based offsets</title> |
| <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/#element-based-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; |
| } |
| .filler { |
| height: 150px; |
| background-color: darkgray; |
| } |
| .offset { |
| height: 50px; |
| background-color: green; |
| } |
| @keyframes expand { |
| from { width: 100px; } |
| to { width: 200px; } |
| } |
| @scroll-timeline timeline_start_start { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1); |
| end: selector(#offset2); |
| } |
| @scroll-timeline timeline_end_end { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1) end; |
| end: selector(#offset2) end; |
| } |
| @scroll-timeline timeline_end_start { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1) end; |
| end: selector(#offset2) start; |
| } |
| @scroll-timeline timeline_end_1_end { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1) end 1; |
| end: selector(#offset2) end; |
| } |
| @scroll-timeline timeline_end_start_05 { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1) end; |
| end: selector(#offset2) 0.5; |
| } |
| @scroll-timeline timeline_start_400px { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset1); |
| end: 400px; |
| } |
| @scroll-timeline timeline_50px_end { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: 50px; |
| end: selector(#offset2) end; |
| } |
| @scroll-timeline timeline_outside { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset_outside); |
| end: auto; |
| } |
| @scroll-timeline timeline_display_none { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#offset_display_none); |
| end: auto; |
| } |
| @scroll-timeline timeline_null_target { |
| source: selector(#scroller); |
| time-range: 10s; |
| start: selector(#no_such_id); |
| end: selector(#no_such_id); |
| } |
| |
| #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_start_start { animation-timeline: timeline_start_start; } |
| #element_end_end { animation-timeline: timeline_end_end; } |
| #element_end_start { animation-timeline: timeline_end_start; } |
| #element_end_1_end { animation-timeline: timeline_end_1_end; } |
| #element_end_start_05 { animation-timeline: timeline_end_start_05; } |
| #element_start_400px { animation-timeline: timeline_start_400px; } |
| #element_50px_end { animation-timeline: timeline_50px_end; } |
| #element_outside { animation-timeline: timeline_outside; } |
| #element_display_none { animation-timeline: timeline_display_none; } |
| #element_null_target { animation-timeline: timeline_null_target; } |
| </style> |
| <div id=scroller> |
| <div id=contents> |
| <div class=filler></div> |
| <div class=offset id=offset1></div> |
| <div class=filler></div> |
| <div class=offset id=offset2></div> |
| <div class=filler></div> |
| </div> |
| </div> |
| <div id=offset_outside></div> |
| <div id=offset_display_none style="display:none"></div> |
| <div id=container> |
| <div id=element_start_start></div> |
| <div id=element_end_end></div> |
| <div id=element_end_start></div> |
| <div id=element_end_1_end></div> |
| <div id=element_end_start_05></div> |
| <div id=element_start_400px></div> |
| <div id=element_50px_end></div> |
| <div id=element_outside></div> |
| <div id=element_display_none></div> |
| <div id=element_null_target></div> |
| </div> |
| <script> |
| |
| // The contents of the scroller looks approximately like this: |
| // |
| // +-------+ |
| // | | |
| // | 150px | filler |
| // | | |
| // +-------+ |
| // +-------+ |
| // | 50px | #offset1 |
| // +-------+ |
| // +-------+ |
| // | | |
| // | 150px | filler |
| // | | |
| // +-------+ |
| // +-------+ |
| // | 50px | #offset2 |
| // +-------+ |
| // +-------+ |
| // | | |
| // | 150px | filler |
| // | | |
| // +-------+ |
| // |
| // The height of the scrollport is 100px. |
| |
| // 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`); |
| } |
| |
| // [200, 400] |
| test_width_at_scroll_top(element_start_start, 0, '0px'); |
| test_width_at_scroll_top(element_start_start, 199, '0px'); |
| test_width_at_scroll_top(element_start_start, 200, '100px'); |
| test_width_at_scroll_top(element_start_start, 300, '150px'); |
| test_width_at_scroll_top(element_start_start, 398, '199px'); |
| test_width_at_scroll_top(element_start_start, 400, '0px'); |
| |
| // [50, 250] |
| test_width_at_scroll_top(element_end_end, 0, '0px'); |
| test_width_at_scroll_top(element_end_end, 49, '0px'); |
| test_width_at_scroll_top(element_end_end, 50, '100px'); |
| test_width_at_scroll_top(element_end_end, 150, '150px'); |
| test_width_at_scroll_top(element_end_end, 248, '199px'); |
| test_width_at_scroll_top(element_end_end, 250, '0px'); |
| |
| // [50, 400] |
| test_width_at_scroll_top(element_end_start, 0, '0px'); |
| test_width_at_scroll_top(element_end_start, 49, '0px'); |
| test_width_at_scroll_top(element_end_start, 50, '100px'); |
| test_width_at_scroll_top(element_end_start, 225, '150px'); |
| test_width_at_scroll_top(element_end_start, 393, '198px'); |
| test_width_at_scroll_top(element_end_start, 400, '0px'); |
| |
| // [100, 250] |
| test_width_at_scroll_top(element_end_1_end, 0, '0px'); |
| test_width_at_scroll_top(element_end_1_end, 99, '0px'); |
| test_width_at_scroll_top(element_end_1_end, 100, '100px'); |
| test_width_at_scroll_top(element_end_1_end, 175, '150px'); |
| test_width_at_scroll_top(element_end_1_end, 247, '198px'); |
| test_width_at_scroll_top(element_end_1_end, 250, '0px'); |
| |
| // [50, 375] |
| test_width_at_scroll_top(element_end_start_05, 0, '0px'); |
| test_width_at_scroll_top(element_end_start_05, 49, '0px'); |
| test_width_at_scroll_top(element_end_start_05, 50, '100px'); |
| test_width_at_scroll_top(element_end_start_05, 206, '148px'); |
| test_width_at_scroll_top(element_end_start_05, 362, '196px'); |
| test_width_at_scroll_top(element_end_start_05, 375, '0px'); |
| |
| // [200, 300] |
| test_width_at_scroll_top(element_start_400px, 0, '0px'); |
| test_width_at_scroll_top(element_start_400px, 199, '0px'); |
| test_width_at_scroll_top(element_start_400px, 200, '100px'); |
| test_width_at_scroll_top(element_start_400px, 300, '150px'); |
| test_width_at_scroll_top(element_start_400px, 398, '199px'); |
| test_width_at_scroll_top(element_start_400px, 400, '0px'); |
| |
| // [50, 250] |
| test_width_at_scroll_top(element_50px_end, 0, '0px'); |
| test_width_at_scroll_top(element_50px_end, 49, '0px'); |
| test_width_at_scroll_top(element_50px_end, 50, '100px'); |
| test_width_at_scroll_top(element_50px_end, 150, '150px'); |
| test_width_at_scroll_top(element_50px_end, 248, '199px'); |
| test_width_at_scroll_top(element_50px_end, 250, '0px'); |
| |
| // Offset not a decendant of scroller (=> no effect value) |
| test_width_at_scroll_top(element_outside, 0, '0px'); |
| test_width_at_scroll_top(element_outside, 100, '0px'); |
| test_width_at_scroll_top(element_outside, 200, '0px'); |
| test_width_at_scroll_top(element_outside, 300, '0px'); |
| test_width_at_scroll_top(element_outside, 400, '0px'); |
| test_width_at_scroll_top(element_outside, 450, '0px'); |
| |
| // Target of element-based offset has no layout box (=> no effect value) |
| test_width_at_scroll_top(element_display_none, 0, '0px'); |
| test_width_at_scroll_top(element_display_none, 100, '0px'); |
| test_width_at_scroll_top(element_display_none, 200, '0px'); |
| test_width_at_scroll_top(element_display_none, 300, '0px'); |
| test_width_at_scroll_top(element_display_none, 400, '0px'); |
| test_width_at_scroll_top(element_display_none, 450, '0px'); |
| |
| // Target of element-based offset is null (=> no effect value) |
| test_width_at_scroll_top(element_null_target, 0, '0px'); |
| test_width_at_scroll_top(element_null_target, 100, '0px'); |
| test_width_at_scroll_top(element_null_target, 200, '0px'); |
| test_width_at_scroll_top(element_null_target, 300, '0px'); |
| test_width_at_scroll_top(element_null_target, 400, '0px'); |
| test_width_at_scroll_top(element_null_target, 450, '0px'); |
| |
| </script> |