view-timeline-range-animation.html (6147B)
1 <!DOCTYPE html> 2 <title>View timelines and animation attachment ranges</title> 3 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range"> 4 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/web-animations/testcommon.js"></script> 8 <script src="support/testcommon.js"></script> 9 <style> 10 @keyframes anim { 11 from { z-index: 0; background-color: skyblue;} 12 to { z-index: 100; background-color: coral; } 13 } 14 #scroller { 15 border: 10px solid lightgray; 16 overflow-y: scroll; 17 width: 200px; 18 height: 200px; 19 } 20 #scroller > div { 21 margin: 800px 0px; 22 width: 100px; 23 height: 100px; 24 } 25 #target { 26 font-size: 10px; 27 background-color: green; 28 z-index: -1; 29 } 30 </style> 31 <main id=main> 32 </main> 33 34 <template id=template_without_scope> 35 <div id=scroller> 36 <div id=target class=timeline></div> 37 </div> 38 </template> 39 40 <template id=template_with_scope> 41 <div id=scope> 42 <div id=target></div> 43 <div id=scroller> 44 <div class=timeline></div> 45 </div> 46 </div> 47 </template> 48 49 <script> 50 setup(assert_implements_animation_timeline); 51 52 function inflate(t, template) { 53 t.add_cleanup(() => main.replaceChildren()); 54 main.append(template.content.cloneNode(true)); 55 } 56 async function scrollTop(e, value) { 57 e.scrollTop = value; 58 await waitForNextFrame(); 59 } 60 async function waitForAnimationReady(target) { 61 await waitForNextFrame(); 62 await Promise.all(target.getAnimations().map(x => x.ready)); 63 } 64 async function assertValueAt(scroller, target, args) { 65 await waitForAnimationReady(target); 66 await scrollTop(scroller, args.scrollTop); 67 assert_equals(getComputedStyle(target).zIndex, args.expected.toString()); 68 } 69 function test_animation_range(options, template, desc_suffix) { 70 if (template === undefined) 71 template = template_without_scope; 72 if (desc_suffix === undefined) 73 desc_suffix = ''; 74 75 promise_test(async (t) => { 76 inflate(t, template); 77 let scroller = main.querySelector('#scroller'); 78 let target = main.querySelector('#target'); 79 let timeline = main.querySelector('.timeline'); 80 let scope = main.querySelector('#scope'); 81 82 if (scope != null) { 83 scope.style.timelineScope = '--t1'; 84 } 85 86 timeline.style.viewTimeline = '--t1'; 87 target.style.animation = 'anim auto linear'; 88 target.style.animationTimeline = '--t1'; 89 target.style.animationRangeStart = options.rangeStart; 90 target.style.animationRangeEnd = options.rangeEnd; 91 92 // Accommodates floating point precision errors at the endpoints. 93 target.style.animationFillMode = 'both'; 94 95 // 0% 96 await assertValueAt(scroller, target, 97 { scrollTop: options.startOffset, expected: 0 }); 98 // 50% 99 await assertValueAt(scroller, target, 100 { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 }); 101 // 100% 102 await assertValueAt(scroller, target, 103 { scrollTop: options.endOffset, expected: 100 }); 104 105 // Test before/after phases (need to clear the fill mode for that). 106 target.style.animationFillMode = 'initial'; 107 await assertValueAt(scroller, target, 108 { scrollTop: options.startOffset - 10, expected: -1 }); 109 await assertValueAt(scroller, target, 110 { scrollTop: options.endOffset + 10, expected: -1 }); 111 // Check 50% again without fill mode. 112 await assertValueAt(scroller, target, 113 { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 }); 114 115 }, `Animation with ranges [${options.rangeStart}, ${options.rangeEnd}] ${desc_suffix}`.trim()); 116 } 117 118 test_animation_range({ 119 rangeStart: 'initial', 120 rangeEnd: 'initial', 121 startOffset: 600, 122 endOffset: 900 123 }); 124 125 test_animation_range({ 126 rangeStart: 'cover 0%', 127 rangeEnd: 'cover 100%', 128 startOffset: 600, 129 endOffset: 900 130 }); 131 132 test_animation_range({ 133 rangeStart: 'contain 0%', 134 rangeEnd: 'contain 100%', 135 startOffset: 700, 136 endOffset: 800 137 }); 138 139 140 test_animation_range({ 141 rangeStart: 'entry 0%', 142 rangeEnd: 'entry 100%', 143 startOffset: 600, 144 endOffset: 700 145 }); 146 147 test_animation_range({ 148 rangeStart: 'exit 0%', 149 rangeEnd: 'exit 100%', 150 startOffset: 800, 151 endOffset: 900 152 }); 153 154 test_animation_range({ 155 rangeStart: 'contain -50%', 156 rangeEnd: 'entry 200%', 157 startOffset: 650, 158 endOffset: 800 159 }); 160 161 test_animation_range({ 162 rangeStart: 'entry 0%', 163 rangeEnd: 'exit 100%', 164 startOffset: 600, 165 endOffset: 900 166 }); 167 168 test_animation_range({ 169 rangeStart: 'cover 20px', 170 rangeEnd: 'cover 100px', 171 startOffset: 620, 172 endOffset: 700 173 }); 174 175 test_animation_range({ 176 rangeStart: 'contain 20px', 177 rangeEnd: 'contain 100px', 178 startOffset: 720, 179 endOffset: 800 180 }); 181 182 test_animation_range({ 183 rangeStart: 'entry 20px', 184 rangeEnd: 'entry 100px', 185 startOffset: 620, 186 endOffset: 700 187 }); 188 189 test_animation_range({ 190 rangeStart: 'entry-crossing 20px', 191 rangeEnd: 'entry-crossing 100px', 192 startOffset: 620, 193 endOffset: 700 194 }); 195 196 test_animation_range({ 197 rangeStart: 'exit 20px', 198 rangeEnd: 'exit 80px', 199 startOffset: 820, 200 endOffset: 880 201 }); 202 203 test_animation_range({ 204 rangeStart: 'exit-crossing 20px', 205 rangeEnd: 'exit-crossing 80px', 206 startOffset: 820, 207 endOffset: 880 208 }); 209 210 test_animation_range({ 211 rangeStart: 'contain 20px', 212 rangeEnd: 'contain calc(100px - 10%)', 213 startOffset: 720, 214 endOffset: 790 215 }); 216 217 test_animation_range({ 218 rangeStart: 'exit 2em', 219 rangeEnd: 'exit 8em', 220 startOffset: 820, 221 endOffset: 880 222 }); 223 224 // Test animation-range via timeline-scope. 225 test_animation_range({ 226 rangeStart: 'exit 2em', 227 rangeEnd: 'exit 8em', 228 startOffset: 820, 229 endOffset: 880 230 }, template_with_scope, '(scoped)'); 231 232 test_animation_range({ 233 rangeStart: 'scroll 100px', 234 rangeEnd: 'scroll 800px', 235 startOffset: 100, 236 endOffset: 800 237 }); 238 239 </script>