| <!DOCTYPE html> |
| <title>Changes to view-timeline are reflected in dependent elements</title> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/web-animations/testcommon.js"></script> |
| <script src="support/testcommon.js"></script> |
| <style> |
| @keyframes anim { |
| from { z-index: 0; } |
| to { z-index: 100; } |
| } |
| .scroller { |
| overflow: hidden; |
| width: 100px; |
| height: 100px; |
| } |
| .scroller > div { |
| height: 100px; |
| } |
| #target { |
| height: 0px; |
| z-index: -1; |
| } |
| </style> |
| <main id=main></main> |
| <script> |
| setup(assert_implements_animation_timeline); |
| |
| function inflate(t, template) { |
| t.add_cleanup(() => main.replaceChildren()); |
| main.append(template.content.cloneNode(true)); |
| main.offsetTop; |
| } |
| async function scrollTop(e, value) { |
| e.scrollTop = value; |
| await waitForNextFrame(); |
| } |
| async function scrollLeft(e, value) { |
| e.scrollLeft = value; |
| await waitForNextFrame(); |
| } |
| </script> |
| |
| <template id=dynamic_view_timeline_attachment> |
| <style> |
| #scroller { |
| timeline-scope: --t1; |
| } |
| .timeline { |
| view-timeline: --t1; |
| } |
| #target { |
| animation: anim 1s linear; |
| animation-timeline: --t1; |
| } |
| </style> |
| <div id=scroller class=scroller> |
| <div id=div75></div> |
| <div id=div25></div> |
| <div id=div_before></div> |
| <div id=target></div> |
| </div> |
| </template> |
| <script> |
| promise_test(async (t) => { |
| inflate(t, dynamic_view_timeline_attachment); |
| |
| await scrollTop(scroller, 50); |
| |
| // scrollTop=50 is 75% for div75. |
| div75.classList.add('timeline'); |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '75', 'div75'); |
| |
| // Identical timelines in div75 and div25 creates an ambiguity. |
| div25.classList.add('timeline'); |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '-1', 'ambiguous'); |
| // Removing the timeline from div75 unambiguously links div25 to the |
| // timeline, making scrollTop=50 at 25% for div25. |
| div75.classList.remove('timeline'); |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '25', 'div25'); |
| |
| // scrollTop=50 is before the timeline start for div_before. |
| div25.classList.remove('timeline'); |
| div_before.classList.add('timeline'); |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '-1', 'ahead of div_before'); |
| // Scroll to 25% (for div_before) to verify that we're linked to that |
| // timeline. |
| await scrollTop(scroller, 150); |
| assert_equals(getComputedStyle(target).zIndex, '25', 'div_before'); |
| |
| // Linking the timeline back to div25 verifies that the new scrollTop=150 is |
| // actually at 75%. |
| div_before.classList.remove('timeline'); |
| div25.classList.add('timeline'); |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '75', 'div25 again'); |
| }, 'Dynamically changing view-timeline attachment'); |
| </script> |
| |
| <template id=dynamic_view_timeline_axis> |
| <style> |
| #timeline { |
| view-timeline: --t1; |
| width: 100px; |
| height: 100px; |
| margin: 100px; |
| } |
| #target { |
| animation: anim 1s linear; |
| animation-timeline: --t1; |
| } |
| </style> |
| <div id=scroller class=scroller> |
| <div id=timeline style="background: red;"> |
| <div id=target></div> |
| </div> |
| </div> |
| </template> |
| <script> |
| promise_test(async (t) => { |
| inflate(t, dynamic_view_timeline_axis); |
| |
| await scrollTop(scroller, 50); // 25% (vertical) |
| await scrollLeft(scroller, 20); // 10% (horizontal) |
| |
| assert_equals(getComputedStyle(target).zIndex, '25', 'vertical'); |
| timeline.style.viewTimelineAxis = 'x'; |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '10', 'horizontal'); |
| }, 'Dynamically changing view-timeline-axis'); |
| </script> |
| |
| <template id=dynamic_view_timeline_inset> |
| <style> |
| #timeline { |
| width: 100px; |
| height: 100px; |
| margin: 100px; |
| view-timeline: --t1; |
| } |
| #target { |
| animation: anim 1s linear; |
| animation-timeline: --t1; |
| } |
| </style> |
| <div id=scroller class=scroller> |
| <div id=timeline style="background: red;"> |
| <div id=target></div> |
| </div> |
| </div> |
| </template> |
| <script> |
| promise_test(async (t) => { |
| inflate(t, dynamic_view_timeline_inset); |
| |
| await scrollTop(scroller, 50); // 25% (without inset). |
| |
| assert_equals(getComputedStyle(target).zIndex, '25', 'without inset'); |
| timeline.style.viewTimelineInset = '0px 50px'; |
| await waitForCSSScrollTimelineStyle(); |
| assert_equals(getComputedStyle(target).zIndex, '0', 'with inset'); |
| }, 'Dynamically changing view-timeline-inset'); |
| </script> |
| |
| <template id=timeline_display_none> |
| <style> |
| #scroller { |
| timeline-scope: --t1; |
| } |
| #timeline { |
| view-timeline: --t1; |
| } |
| #target { |
| animation: anim 1s linear; |
| animation-timeline: --t1; |
| } |
| </style> |
| <div id=scroller class=scroller> |
| <div></div> |
| <div id=timeline></div> |
| <div id=target></div> |
| </div> |
| </template> |
| <script> |
| promise_test(async (t) => { |
| inflate(t, timeline_display_none); |
| |
| await scrollTop(scroller, 50); |
| assert_equals(getComputedStyle(target).zIndex, '25', 'display:block'); |
| timeline.style.display = 'none'; |
| await waitForNextFrame(); |
| // The timeline became inactive. |
| assert_equals(getComputedStyle(target).zIndex, '-1', 'display:none'); |
| }, 'Element with scoped view-timeline becoming display:none'); |
| </script> |