animation-timeline-view-functional-notation.tentative.html (19460B)
1 <!DOCTYPE html> 2 <title>The animation-timeline: view() notation</title> 3 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> 4 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-notation"> 5 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/7587"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/web-animations/testcommon.js"></script> 9 <script src="support/testcommon.js"></script> 10 <style> 11 @keyframes fade-in-out-without-timeline-range { 12 0% { opacity: 0; } 13 40% { opacity: 1; } 14 60% { opacity: 1; } 15 100% { opacity: 0; } 16 } 17 @keyframes fade-out-without-timeline-range { 18 0% { opacity: 1; } 19 100% { opacity: 0; } 20 } 21 @keyframes change-font-size-without-timeline-range { 22 0% { font-size: 10px; } 23 100% { font-size: 30px; } 24 } 25 @keyframes fade-in-out { 26 entry 0% { opacity: 0; } 27 entry 100% { opacity: 1; } 28 exit 0% { opacity: 1; } 29 exit 100% { opacity: 0; } 30 } 31 @keyframes fade-out { 32 exit 0% { opacity: 1; } 33 exit 100% { opacity: 0; } 34 } 35 @keyframes change-font-size { 36 exit 0% { font-size: 10px; } 37 exit 100% { font-size: 20px; } 38 } 39 #container { 40 width: 200px; 41 height: 200px; 42 overflow-y: scroll; 43 overflow-x: hidden; 44 } 45 .target { 46 width: 100px; 47 height: 100px; 48 background-color: red; 49 } 50 .content { 51 width: 400px; 52 height: 400px; 53 background-color: blue; 54 } 55 </style> 56 57 <body> 58 <script> 59 "use strict"; 60 61 setup(assert_implements_animation_timeline); 62 63 const createTargetWithStuff = function(t, divClasses) { 64 let container = document.createElement('div'); 65 container.id = 'container'; 66 document.body.appendChild(container); 67 68 // *** When testing inset 69 // <div id='container'> 70 // <div class='content'></div> 71 // <div class='target'></div> 72 // <div class='content'></div> 73 // </div> 74 // *** When testing axis 75 // <div id='container'> 76 // <div class='target'></div> 77 // <div class='content'></div> 78 // </div> 79 80 let divs = []; 81 let target; 82 for(let className of divClasses) { 83 let div = document.createElement('div'); 84 div.className = className; 85 container.appendChild(div); 86 87 divs.push(div); 88 if(className === 'target') 89 target = div; 90 } 91 92 if (t && typeof t.add_cleanup === 'function') { 93 t.add_cleanup(() => { 94 for(let div of divs) 95 div.remove(); 96 container.remove(); 97 }); 98 } 99 100 return [container, target]; 101 }; 102 103 async function scrollLeft(element, value) { 104 element.scrollLeft = value; 105 await waitForNextFrame(); 106 } 107 108 async function scrollTop(element, value) { 109 element.scrollTop = value; 110 await waitForNextFrame(); 111 } 112 113 // --------------------------------- 114 // Tests without timeline range name 115 // --------------------------------- 116 117 promise_test(async t => { 118 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 119 await runAndWaitForFrameUpdate(() => { 120 container.style.overflow = 'hidden'; 121 div.style.animation = "fade-in-out-without-timeline-range 1s linear forwards"; 122 div.style.animationTimeline = "view()"; 123 124 }); 125 // So the range is [200px, 500px]. 126 await scrollTop(container, 200); 127 assert_equals(getComputedStyle(div).opacity, '0', 'At 0%'); 128 await scrollTop(container, 260); 129 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%'); 130 await scrollTop(container, 320); 131 assert_equals(getComputedStyle(div).opacity, '1', 'At 40%'); 132 133 await scrollTop(container, 380); 134 assert_equals(getComputedStyle(div).opacity, '1', 'At 60%'); 135 await scrollTop(container, 440); 136 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%'); 137 await scrollTop(container, 500); 138 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 139 }, 'animation-timeline: view() without timeline range name'); 140 141 promise_test(async t => { 142 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 143 await runAndWaitForFrameUpdate(() => { 144 container.style.overflow = 'hidden'; 145 div.style.animation = "fade-in-out-without-timeline-range 1s linear forwards"; 146 div.style.animationTimeline = "view(50px)"; 147 }); 148 // So the range is [250px, 450px]. 149 150 await scrollTop(container, 250); 151 assert_equals(getComputedStyle(div).opacity, '0', 'At 0%'); 152 await scrollTop(container, 290); 153 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%'); 154 await scrollTop(container, 330); 155 assert_equals(getComputedStyle(div).opacity, '1', 'At 40%'); 156 157 await scrollTop(container, 370); 158 assert_equals(getComputedStyle(div).opacity, '1', 'At 60%'); 159 await scrollTop(container, 410); 160 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%'); 161 await scrollTop(container, 450); 162 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 163 }, 'animation-timeline: view(50px) without timeline range name'); 164 165 promise_test(async t => { 166 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 167 await runAndWaitForFrameUpdate(() => { 168 container.style.overflow = 'hidden'; 169 div.style.animation = "fade-in-out-without-timeline-range 1s linear forwards"; 170 div.style.animationTimeline = "view(auto 50px)"; 171 }); 172 // So the range is [250px, 500px]. 173 174 await scrollTop(container, 250); 175 assert_equals(getComputedStyle(div).opacity, '0', 'At 0%'); 176 await scrollTop(container, 300); 177 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 20%'); 178 await scrollTop(container, 350); 179 assert_equals(getComputedStyle(div).opacity, '1', 'At 40%'); 180 181 await scrollTop(container, 400); 182 assert_equals(getComputedStyle(div).opacity, '1', 'At 60%'); 183 await scrollTop(container, 450); 184 assert_equals(getComputedStyle(div).opacity, '0.5', 'At 80%'); 185 await scrollTop(container, 500); 186 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 187 }, 'animation-timeline: view(auto 50px) without timeline range name'); 188 189 promise_test(async t => { 190 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 191 await runAndWaitForFrameUpdate(() => { 192 container.style.overflow = 'hidden'; 193 div.style.animation = "fade-out-without-timeline-range 1s linear forwards"; 194 div.style.animationTimeline = "view(inline)"; 195 }); 196 // So the range is [-200px, 100px], but it is impossible to scroll to the 197 // negative part. 198 199 await scrollLeft(container, 0); 200 assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333, 201 0.00001, 'At 66.7%'); 202 // Note: 20% for each 60px. 203 await scrollLeft(container, 40); 204 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%'); 205 await scrollLeft(container, 100); 206 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 207 }, 'animation-timeline: view(inline) without timeline range name'); 208 209 promise_test(async t => { 210 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 211 await runAndWaitForFrameUpdate(() => { 212 container.style.overflow = 'hidden'; 213 div.style.animation = "fade-out-without-timeline-range 1s linear forwards"; 214 div.style.animationTimeline = "view(x)"; 215 }); 216 // So the range is [-200px, 100px], but it is impossible to scroll to the 217 // negative part. 218 219 await scrollLeft(container, 0); 220 assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333, 221 0.00001, 'At 66.7%'); 222 // Note: 20% for each 60px. 223 await scrollLeft(container, 40); 224 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%'); 225 await scrollLeft(container, 100); 226 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 227 }, 'animation-timeline: view(x) without timeline range name'); 228 229 promise_test(async t => { 230 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 231 await runAndWaitForFrameUpdate(() => { 232 div.style.animation = "fade-out-without-timeline-range 1s linear forwards"; 233 div.style.animationTimeline = "view(y)"; 234 }); 235 // So the range is [-200px, 100px], but it is impossible to scroll to the 236 // negative part. 237 238 await scrollTop(container, 0); 239 assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333, 240 0.00001, 'At 66.7%'); 241 // Note: 20% for each 60px. 242 await scrollTop(container, 40); 243 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%'); 244 await scrollTop(container, 100); 245 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 246 }, 'animation-timeline: view(y) without timeline range name'); 247 248 promise_test(async t => { 249 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 250 await runAndWaitForFrameUpdate(() => { 251 container.style.overflow = 'hidden'; 252 div.style.animation = "fade-out-without-timeline-range 1s linear forwards"; 253 div.style.animationTimeline = "view(x 50px)"; 254 }); 255 // So the range is [-150px, 50px], but it is impossible to scroll to the 256 // negative part. 257 258 // Note: 25% for each 50px. 259 await scrollLeft(container, 0); 260 assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75%'); 261 await scrollLeft(container, 10); 262 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%'); 263 await scrollLeft(container, 50); 264 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 265 }, 'animation-timeline: view(x 50px) without timeline range name'); 266 267 promise_test(async t => { 268 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 269 await runAndWaitForFrameUpdate(() => { 270 container.style.overflow = 'hidden'; 271 div.style.animation 272 = "fade-out-without-timeline-range 1s linear forwards, " + 273 "change-font-size-without-timeline-range 1s linear forwards"; 274 div.style.animationTimeline = "view(50px), view(inline 50px)"; 275 }); 276 277 await scrollLeft(container, 0); 278 assert_equals(getComputedStyle(div).fontSize, '25px', 'At 75% inline'); 279 await scrollLeft(container, 10); 280 assert_equals(getComputedStyle(div).fontSize, '26px', 'At 80% inline'); 281 await scrollLeft(container, 50); 282 assert_equals(getComputedStyle(div).fontSize, '30px', 'At 100% inline'); 283 284 await scrollLeft(container, 0); 285 286 await scrollTop(container, 0); 287 assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75% block'); 288 await scrollTop(container, 10); 289 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80% block'); 290 await scrollTop(container, 50); 291 assert_equals(getComputedStyle(div).opacity, '0', 'At 100% block'); 292 293 await scrollLeft(container, 10); 294 await scrollTop(container, 10); 295 assert_equals(getComputedStyle(div).fontSize, '26px', 'At 80% inline'); 296 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80% block'); 297 }, 'animation-timeline: view(50px), view(inline 50px) without timeline range ' + 298 'name'); 299 300 promise_test(async t => { 301 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 302 await runAndWaitForFrameUpdate(() => { 303 container.style.overflow = 'hidden'; 304 div.style.animation = "fade-out-without-timeline-range 1s linear forwards"; 305 div.style.animationTimeline = "view(inline)"; 306 }); 307 await scrollLeft(container, 0); 308 assert_approx_equals(parseFloat(getComputedStyle(div).opacity), 0.33333, 309 0.00001, 'At 66.7%'); 310 await scrollLeft(container, 40); 311 assert_equals(getComputedStyle(div).opacity, '0.2', 'At 80%'); 312 await scrollLeft(container, 100); 313 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 314 315 div.style.animationTimeline = "view(inline 50px)"; 316 await scrollLeft(container, 0); 317 assert_equals(getComputedStyle(div).opacity, '0.25', 'At 75%'); 318 await scrollLeft(container, 50); 319 assert_equals(getComputedStyle(div).opacity, '0', 'At 100%'); 320 }, 'animation-timeline: view(inline) changes to view(inline 50px), without' + 321 'timeline range name'); 322 323 324 // --------------------------------- 325 // Tests with timeline range name 326 // --------------------------------- 327 328 promise_test(async t => { 329 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 330 await runAndWaitForFrameUpdate(() => { 331 div.style.animation = "fade-in-out 1s linear forwards"; 332 div.style.animationTimeline = "view()"; 333 }); 334 335 await scrollTop(container, 200); 336 assert_equals(getComputedStyle(div).opacity, '0', 'At entry 0%'); 337 await scrollTop(container, 250); 338 assert_equals(getComputedStyle(div).opacity, '0.5', 'At entry 50%'); 339 await scrollTop(container, 300); 340 assert_equals(getComputedStyle(div).opacity, '1', 'At entry 100%'); 341 342 await scrollTop(container, 400); 343 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 344 await scrollTop(container, 450); 345 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 346 await scrollTop(container, 500); 347 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 348 }, 'animation-timeline: view()'); 349 350 promise_test(async t => { 351 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 352 await runAndWaitForFrameUpdate(() => { 353 div.style.animation = "fade-in-out 1s linear forwards"; 354 div.style.animationTimeline = "view(50px)"; 355 }); 356 357 await scrollTop(container, 250); 358 assert_equals(getComputedStyle(div).opacity, '0', 'At entry 0%'); 359 await scrollTop(container, 300); 360 assert_equals(getComputedStyle(div).opacity, '0.5', 'At entry 50%'); 361 362 await scrollTop(container, 350); 363 assert_equals(getComputedStyle(div).opacity, '1', 'At entry 100% & exit 0%'); 364 365 await scrollTop(container, 400); 366 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 367 await scrollTop(container, 450); 368 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 369 }, 'animation-timeline: view(50px)'); 370 371 promise_test(async t => { 372 let [container, div] = createTargetWithStuff(t, ['content', 'target', 'content']); 373 await runAndWaitForFrameUpdate(() => { 374 div.style.animation = "fade-in-out 1s linear forwards"; 375 div.style.animationTimeline = "view(auto 50px)"; 376 }); 377 378 await scrollTop(container, 250); 379 assert_equals(getComputedStyle(div).opacity, '0', 'At entry 0%'); 380 await scrollTop(container, 300); 381 assert_equals(getComputedStyle(div).opacity, '0.5', 'At entry 50%'); 382 await scrollTop(container, 350); 383 assert_equals(getComputedStyle(div).opacity, '1', 'At entry 100%'); 384 385 await scrollTop(container, 400); 386 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 387 await scrollTop(container, 450); 388 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 389 await scrollTop(container, 500); 390 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 391 }, 'animation-timeline: view(auto 50px)'); 392 393 promise_test(async t => { 394 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 395 await runAndWaitForFrameUpdate(() => { 396 container.style.overflow = 'scroll'; 397 div.style.animation = "fade-out 1s linear forwards"; 398 div.style.animationTimeline = "view(inline)"; 399 }); 400 401 await scrollLeft(container, 0); 402 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 403 await scrollLeft(container, 50); 404 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 405 await scrollLeft(container, 100); 406 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 407 }, 'animation-timeline: view(inline)'); 408 409 promise_test(async t => { 410 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 411 await runAndWaitForFrameUpdate(() => { 412 container.style.overflow = 'scroll'; 413 div.style.animation = "fade-out 1s linear forwards"; 414 div.style.animationTimeline = "view(x)"; 415 }); 416 417 await scrollLeft(container, 0); 418 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 419 await scrollLeft(container, 50); 420 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 421 await scrollLeft(container, 100); 422 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 423 }, 'animation-timeline: view(x)'); 424 425 promise_test(async t => { 426 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 427 await runAndWaitForFrameUpdate(() => { 428 container.style.overflow = 'scroll'; 429 div.style.animation = "fade-out 1s linear forwards"; 430 div.style.animationTimeline = "view(y)"; 431 }); 432 433 await scrollTop(container, 0); 434 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 435 await scrollTop(container, 50); 436 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 437 await scrollTop(container, 100); 438 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 439 }, 'animation-timeline: view(y)'); 440 441 promise_test(async t => { 442 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 443 await runAndWaitForFrameUpdate(() => { 444 container.style.overflowY = 'hidden'; 445 container.style.overflowX = 'scroll'; 446 div.style.animation = "fade-out 1s linear forwards"; 447 div.style.animationTimeline = "view(x 50px)"; 448 }); 449 450 await scrollLeft(container, 0); 451 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 452 await scrollLeft(container, 50); 453 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 454 }, 'animation-timeline: view(x 50px)'); 455 456 promise_test(async t => { 457 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 458 await runAndWaitForFrameUpdate(() => { 459 container.style.overflow = 'scroll'; 460 div.style.animation 461 = "fade-out 1s linear forwards, change-font-size 1s linear forwards"; 462 div.style.animationTimeline = "view(), view(inline)"; 463 }); 464 465 const assert_font_size_equals = (expected, description) => { 466 assert_approx_equals(parseFloat(getComputedStyle(div).fontSize), parseFloat(expected), 0.0001, 'At exit 0% inline'); 467 }; 468 469 await scrollLeft(container, 0); 470 assert_font_size_equals('10px', 'At exit 0% inline'); 471 await scrollLeft(container, 50); 472 assert_font_size_equals('15px', 'At exit 50% inline'); 473 await scrollLeft(container, 100); 474 assert_font_size_equals('20px', 'At exit 100% inline'); 475 476 await scrollLeft(container, 0); 477 478 await scrollTop(container, 0); 479 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0% block'); 480 await scrollTop(container, 50); 481 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50% block'); 482 await scrollTop(container, 100); 483 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100% block'); 484 485 await scrollLeft(container, 50); 486 await scrollTop(container, 50); 487 assert_font_size_equals('15px', 'At exit 50% inline'); 488 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50% block'); 489 }, 'animation-timeline: view(), view(inline)'); 490 491 promise_test(async t => { 492 let [container, div] = createTargetWithStuff(t, ['target', 'content']); 493 await runAndWaitForFrameUpdate(() => { 494 container.style.overflowY = 'hidden'; 495 container.style.overflowX = 'scroll'; 496 div.style.animation = "fade-out 1s linear forwards"; 497 }); 498 499 div.style.animationTimeline = "view(inline)"; 500 await scrollLeft(container, 0); 501 assert_equals(getComputedStyle(div).opacity, '1', 'At exit 0%'); 502 await scrollLeft(container, 50); 503 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 504 await scrollLeft(container, 100); 505 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 506 507 div.style.animationTimeline = "view(inline 50px)"; 508 await scrollLeft(container, 0); 509 assert_equals(getComputedStyle(div).opacity, '0.5', 'At exit 50%'); 510 await scrollLeft(container, 50); 511 assert_equals(getComputedStyle(div).opacity, '0', 'At exit 100%'); 512 }, 'animation-timeline: view(inline) changes to view(inline 50px)'); 513 514 </script> 515 </body>