| <!DOCTYPE html> |
| <title>@scroll-timeline element offset invalidation</title> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#typedef-element-offset"> |
| <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; |
| } |
| #scroller > div { |
| height: 50px; |
| } |
| @keyframes expand { |
| from { width: 100px; } |
| to { width: 200px; } |
| } |
| @scroll-timeline timeline { |
| source: selector(#scroller); |
| start: selector(#offset1) end; |
| end: selector(#offset2) end; |
| } |
| #element { |
| width: 0px; |
| height: 20px; |
| animation: expand 1000s linear; |
| animation-timeline: timeline; |
| } |
| /* Ensure stable expectations if feature is not supported */ |
| @supports not (animation-timeline:foo) { |
| #element { animation-play-state: paused; } |
| } |
| </style> |
| <div id=scroller></div> |
| <div id=element></div> |
| <p class=sibling1></p> |
| <p class=sibling2></p> |
| <script> |
| |
| function setup() { |
| while (scroller.firstChild) |
| scroller.firstChild.remove(); |
| for (let i = 0; i < 10; i++) |
| scroller.append(document.createElement('div')); |
| } |
| |
| // The contents of the scroller look like this: |
| // |
| // +-------+ |
| // | 50px | div (0) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (1) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (2) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (3) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (4) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (5) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (6) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (7) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (8) |
| // +-------+ |
| // +-------+ |
| // | 50px | div (9) |
| // +-------+ |
| // |
| // The height of the scrollport is 100px. |
| |
| function invalidation_test(func, description) { |
| promise_test(async (t) => { |
| setup(); |
| await func(); |
| }, description); |
| } |
| |
| function remove(id) { |
| let old_element = document.getElementById(id); |
| if (old_element) |
| old_element.removeAttribute('id'); |
| } |
| |
| function reassign(id, element) { |
| remove(id); |
| element.setAttribute('id', id); |
| } |
| |
| async function assert_element_width_at_scroll(expected_width, scroll) { |
| scroller.scrollTop = scroll; |
| await waitForNextFrame(); |
| assert_equals(getComputedStyle(element).width, expected_width); |
| } |
| |
| invalidation_test(async () => { |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Offsets missing'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| // [100, 150] |
| reassign('offset1', scroller.children[4]); |
| await assert_element_width_at_scroll('100px', 100); |
| }, 'Change first offset'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| // [50, 250] |
| reassign('offset2', scroller.children[7]); |
| await assert_element_width_at_scroll('125px', 100); |
| }, 'Change second offset'); |
| |
| invalidation_test(async () => { |
| // [50, 250] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[7]); |
| await assert_element_width_at_scroll('125px', 100); |
| |
| // [0, 200] |
| reassign('offset1', scroller.children[2]); |
| reassign('offset2', scroller.children[4]); |
| await assert_element_width_at_scroll('150px', 50); |
| }, 'Change both offsets'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| remove('offset1'); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Remove first offset'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| remove('offset2'); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Remove second offset'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| remove('offset1'); |
| remove('offset2'); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Remove both offsets'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| reassign('offset1', document.querySelector('.sibling1')); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Reassign first offset to sibling of scroller'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| reassign('offset2', document.querySelector('.sibling2')); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Reassign second offset to sibling of scroller'); |
| |
| invalidation_test(async () => { |
| // [50, 150] |
| reassign('offset1', scroller.children[3]); |
| reassign('offset2', scroller.children[5]); |
| await assert_element_width_at_scroll('150px', 100); |
| |
| reassign('offset1', document.querySelector('.sibling1')); |
| reassign('offset2', document.querySelector('.sibling2')); |
| await assert_element_width_at_scroll('0px', 0); |
| }, 'Reassign both offsets to sibling of scroller'); |
| </script> |