| <!DOCTYPE html> |
| <title>View timelines and animation attachment ranges</title> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range"> |
| <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range"> |
| <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; background-color: skyblue;} |
| to { z-index: 100; background-color: coral; } |
| } |
| #scroller { |
| border: 10px solid lightgray; |
| overflow-y: scroll; |
| width: 200px; |
| height: 200px; |
| } |
| #scroller > div { |
| margin: 800px 0px; |
| width: 100px; |
| height: 100px; |
| } |
| #target { |
| font-size: 10px; |
| background-color: green; |
| z-index: -1; |
| } |
| </style> |
| <main id=main> |
| </main> |
| |
| <template id=template_without_scope> |
| <div id=scroller> |
| <div id=target class=timeline></div> |
| </div> |
| </template> |
| |
| <template id=template_with_scope> |
| <div id=scope> |
| <div id=target></div> |
| <div id=scroller> |
| <div class=timeline></div> |
| </div> |
| </div> |
| </template> |
| |
| <script> |
| setup(assert_implements_animation_timeline); |
| |
| function inflate(t, template) { |
| t.add_cleanup(() => main.replaceChildren()); |
| main.append(template.content.cloneNode(true)); |
| } |
| async function scrollTop(e, value) { |
| e.scrollTop = value; |
| await waitForNextFrame(); |
| } |
| async function waitForAnimationReady(target) { |
| await waitForNextFrame(); |
| await Promise.all(target.getAnimations().map(x => x.ready)); |
| } |
| async function assertValueAt(scroller, target, args) { |
| await waitForAnimationReady(target); |
| await scrollTop(scroller, args.scrollTop); |
| assert_equals(getComputedStyle(target).zIndex, args.expected.toString()); |
| } |
| function test_animation_range(options, template, desc_suffix) { |
| if (template === undefined) |
| template = template_without_scope; |
| if (desc_suffix === undefined) |
| desc_suffix = ''; |
| |
| promise_test(async (t) => { |
| inflate(t, template); |
| let scroller = main.querySelector('#scroller'); |
| let target = main.querySelector('#target'); |
| let timeline = main.querySelector('.timeline'); |
| let scope = main.querySelector('#scope'); |
| |
| if (scope != null) { |
| scope.style.timelineScope = '--t1'; |
| } |
| |
| timeline.style.viewTimeline = '--t1'; |
| target.style.animation = 'anim auto linear'; |
| target.style.animationTimeline = '--t1'; |
| target.style.animationRangeStart = options.rangeStart; |
| target.style.animationRangeEnd = options.rangeEnd; |
| |
| // Accommodates floating point precision errors at the endpoints. |
| target.style.animationFillMode = 'both'; |
| |
| // 0% |
| await assertValueAt(scroller, target, |
| { scrollTop: options.startOffset, expected: 0 }); |
| // 50% |
| await assertValueAt(scroller, target, |
| { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 }); |
| // 100% |
| await assertValueAt(scroller, target, |
| { scrollTop: options.endOffset, expected: 100 }); |
| |
| // Test before/after phases (need to clear the fill mode for that). |
| target.style.animationFillMode = 'initial'; |
| await assertValueAt(scroller, target, |
| { scrollTop: options.startOffset - 10, expected: -1 }); |
| await assertValueAt(scroller, target, |
| { scrollTop: options.endOffset + 10, expected: -1 }); |
| // Check 50% again without fill mode. |
| await assertValueAt(scroller, target, |
| { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 }); |
| |
| }, `Animation with ranges [${options.rangeStart}, ${options.rangeEnd}] ${desc_suffix}`.trim()); |
| } |
| |
| test_animation_range({ |
| rangeStart: 'initial', |
| rangeEnd: 'initial', |
| startOffset: 600, |
| endOffset: 900 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'cover 0%', |
| rangeEnd: 'cover 100%', |
| startOffset: 600, |
| endOffset: 900 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'contain 0%', |
| rangeEnd: 'contain 100%', |
| startOffset: 700, |
| endOffset: 800 |
| }); |
| |
| |
| test_animation_range({ |
| rangeStart: 'entry 0%', |
| rangeEnd: 'entry 100%', |
| startOffset: 600, |
| endOffset: 700 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'exit 0%', |
| rangeEnd: 'exit 100%', |
| startOffset: 800, |
| endOffset: 900 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'contain -50%', |
| rangeEnd: 'entry 200%', |
| startOffset: 650, |
| endOffset: 800 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'entry 0%', |
| rangeEnd: 'exit 100%', |
| startOffset: 600, |
| endOffset: 900 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'cover 20px', |
| rangeEnd: 'cover 100px', |
| startOffset: 620, |
| endOffset: 700 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'contain 20px', |
| rangeEnd: 'contain 100px', |
| startOffset: 720, |
| endOffset: 800 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'entry 20px', |
| rangeEnd: 'entry 100px', |
| startOffset: 620, |
| endOffset: 700 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'entry-crossing 20px', |
| rangeEnd: 'entry-crossing 100px', |
| startOffset: 620, |
| endOffset: 700 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'exit 20px', |
| rangeEnd: 'exit 80px', |
| startOffset: 820, |
| endOffset: 880 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'exit-crossing 20px', |
| rangeEnd: 'exit-crossing 80px', |
| startOffset: 820, |
| endOffset: 880 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'contain 20px', |
| rangeEnd: 'contain calc(100px - 10%)', |
| startOffset: 720, |
| endOffset: 790 |
| }); |
| |
| test_animation_range({ |
| rangeStart: 'exit 2em', |
| rangeEnd: 'exit 8em', |
| startOffset: 820, |
| endOffset: 880 |
| }); |
| |
| // Test animation-range via timeline-scope. |
| test_animation_range({ |
| rangeStart: 'exit 2em', |
| rangeEnd: 'exit 8em', |
| startOffset: 820, |
| endOffset: 880 |
| }, template_with_scope, '(scoped)'); |
| |
| test_animation_range({ |
| rangeStart: 'scroll 100px', |
| rangeEnd: 'scroll 800px', |
| startOffset: 100, |
| endOffset: 800 |
| }); |
| |
| </script> |