AnimationEffect-getComputedTiming.tentative.html (22687B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>AnimationEffect.getComputedTiming() for CSS animations</title> 4 <!-- TODO: Add a more specific link for this once it is specified. --> 5 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="support/testcommon.js"></script> 9 <style> 10 @keyframes moveAnimation { 11 from { margin-left: 100px } 12 to { margin-left: 200px } 13 } 14 </style> 15 <body> 16 <div id="log"></div> 17 <script> 18 19 'use strict'; 20 21 // -------------------- 22 // delay 23 // -------------------- 24 25 test(t => { 26 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 27 const effect = div.getAnimations()[0].effect; 28 assert_equals(effect.getComputedTiming().delay, 0, 'Initial value of delay'); 29 }, 'delay of a new animation'); 30 31 test(t => { 32 const div = addDiv(t, { style: 'animation: moveAnimation 100s -10s' }); 33 const effect = div.getAnimations()[0].effect; 34 assert_equals(effect.getComputedTiming().delay, -10 * MS_PER_SEC, 35 'Initial value of delay'); 36 }, 'Negative delay of a new animation'); 37 38 test(t => { 39 const div = addDiv(t, { style: 'animation: moveAnimation 100s 10s' }); 40 const effect = div.getAnimations()[0].effect; 41 assert_equals(effect.getComputedTiming().delay, 10 * MS_PER_SEC, 42 'Initial value of delay'); 43 }, 'Positive delay of a new animation'); 44 45 46 // -------------------- 47 // endDelay 48 // -------------------- 49 50 test(t => { 51 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 52 const effect = div.getAnimations()[0].effect; 53 assert_equals(effect.getComputedTiming().endDelay, 0, 54 'Initial value of endDelay'); 55 }, 'endDelay of a new animation'); 56 57 58 // -------------------- 59 // fill 60 // -------------------- 61 62 test(t => { 63 const getEffectWithFill = fill => { 64 const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + fill }); 65 return div.getAnimations()[0].effect; 66 }; 67 68 let effect = getEffectWithFill(''); 69 assert_equals(effect.getComputedTiming().fill, 'none', 70 'Initial value of fill'); 71 effect = getEffectWithFill('forwards'); 72 assert_equals(effect.getComputedTiming().fill, 'forwards', 73 'Fill forwards'); 74 effect = getEffectWithFill('backwards'); 75 assert_equals(effect.getComputedTiming().fill, 'backwards', 76 'Fill backwards'); 77 effect = getEffectWithFill('both'); 78 assert_equals(effect.getComputedTiming().fill, 'both', 79 'Fill forwards and backwards'); 80 }, 'fill of a new animation'); 81 82 83 // -------------------- 84 // iterationStart 85 // -------------------- 86 87 test(t => { 88 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 89 const effect = div.getAnimations()[0].effect; 90 assert_equals(effect.getComputedTiming().iterationStart, 0, 91 'Initial value of iterationStart'); 92 }, 'iterationStart of a new animation'); 93 94 95 // -------------------- 96 // iterations 97 // -------------------- 98 99 test(t => { 100 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 101 const effect = div.getAnimations()[0].effect; 102 assert_equals(effect.getComputedTiming().iterations, 1, 103 'Initial value of iterations'); 104 }, 'iterations of a new animation'); 105 106 test(t => { 107 const div = addDiv(t, { style: 'animation: moveAnimation 100s 2016.5' }); 108 const effect = div.getAnimations()[0].effect; 109 assert_equals(effect.getComputedTiming().iterations, 2016.5, 110 'Initial value of iterations'); 111 }, 'iterations of a finitely repeating animation'); 112 113 test(t => { 114 const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' }); 115 const effect = div.getAnimations()[0].effect; 116 assert_equals(effect.getComputedTiming().iterations, Infinity, 117 'Initial value of iterations'); 118 }, 'iterations of an infinitely repeating animation'); 119 120 121 // -------------------- 122 // duration 123 // -------------------- 124 125 test(t => { 126 const div = addDiv(t, { 127 style: 'animation: moveAnimation 100s -10s infinite' 128 }); 129 const effect = div.getAnimations()[0].effect; 130 assert_equals(effect.getComputedTiming().duration, 100 * MS_PER_SEC, 131 'Initial value of duration'); 132 }, 'duration of a new animation'); 133 134 135 // -------------------- 136 // direction 137 // -------------------- 138 139 test(t => { 140 const getEffectWithDir = dir => { 141 const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + dir }); 142 return div.getAnimations()[0].effect; 143 }; 144 145 let effect = getEffectWithDir(''); 146 assert_equals(effect.getComputedTiming().direction, 'normal', 147 'Initial value of normal direction'); 148 effect = getEffectWithDir('reverse'); 149 assert_equals(effect.getComputedTiming().direction, 'reverse', 150 'Initial value of reverse direction'); 151 effect = getEffectWithDir('alternate'); 152 assert_equals(effect.getComputedTiming().direction, 'alternate', 153 'Initial value of alternate direction'); 154 effect = getEffectWithDir('alternate-reverse'); 155 assert_equals(effect.getComputedTiming().direction, 'alternate-reverse', 156 'Initial value of alternate-reverse direction'); 157 }, 'direction of a new animation'); 158 159 160 // -------------------- 161 // easing 162 // -------------------- 163 164 test(t => { 165 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 166 const effect = div.getAnimations()[0].effect; 167 assert_equals(effect.getComputedTiming().easing, 'linear', 168 'Initial value of easing'); 169 }, 'easing of a new animation'); 170 171 172 // ------------------------------ 173 // endTime 174 // = max(start delay + active duration + end delay, 0) 175 // -------------------- 176 177 test(t => { 178 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 179 const effect = div.getAnimations()[0].effect; 180 assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC, 181 'Initial value of endTime'); 182 }, 'endTime of an new animation'); 183 184 test(t => { 185 const div = addDiv(t, { style: 'animation: moveAnimation 100s -5s' }); 186 const effect = div.getAnimations()[0].effect; 187 const answer = (100 - 5) * MS_PER_SEC; 188 assert_equals(effect.getComputedTiming().endTime, answer, 189 'Initial value of endTime'); 190 }, 'endTime of an animation with a negative delay'); 191 192 test(t => { 193 const div = addDiv(t, { 194 style: 'animation: moveAnimation 10s -100s infinite' 195 }); 196 const effect = div.getAnimations()[0].effect; 197 assert_equals(effect.getComputedTiming().endTime, Infinity, 198 'Initial value of endTime'); 199 }, 'endTime of an infinitely repeating animation'); 200 201 test(t => { 202 const div = addDiv(t, { style: 'animation: moveAnimation 0s 100s infinite' }); 203 const effect = div.getAnimations()[0].effect; 204 assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC, 205 'Initial value of endTime'); 206 }, 'endTime of an infinitely repeating zero-duration animation'); 207 208 test(t => { 209 // Fill forwards so div.getAnimations()[0] won't return an 210 // undefined value. 211 const div = addDiv(t, { 212 style: 'animation: moveAnimation 10s -100s forwards' 213 }); 214 const effect = div.getAnimations()[0].effect; 215 assert_equals(effect.getComputedTiming().endTime, 0, 216 'Initial value of endTime'); 217 }, 'endTime of an animation that finishes before its startTime'); 218 219 220 // -------------------- 221 // activeDuration 222 // = iteration duration * iteration count 223 // -------------------- 224 225 test(t => { 226 const div = addDiv(t, { style: 'animation: moveAnimation 100s 5' }); 227 const effect = div.getAnimations()[0].effect; 228 const answer = 100 * MS_PER_SEC * 5; 229 assert_equals(effect.getComputedTiming().activeDuration, answer, 230 'Initial value of activeDuration'); 231 }, 'activeDuration of a new animation'); 232 233 test(t => { 234 const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' }); 235 const effect = div.getAnimations()[0].effect; 236 assert_equals(effect.getComputedTiming().activeDuration, Infinity, 237 'Initial value of activeDuration'); 238 }, 'activeDuration of an infinitely repeating animation'); 239 240 test(t => { 241 const div = addDiv(t, { style: 'animation: moveAnimation 0s 1s infinite' }); 242 const effect = div.getAnimations()[0].effect; 243 // If either the iteration duration or iteration count are zero, 244 // the active duration is zero. 245 assert_equals(effect.getComputedTiming().activeDuration, 0, 246 'Initial value of activeDuration'); 247 }, 'activeDuration of an infinitely repeating zero-duration animation'); 248 249 test(t => { 250 const div = addDiv(t, { style: 'animation: moveAnimation 100s 1s 0' }); 251 const effect = div.getAnimations()[0].effect; 252 // If either the iteration duration or iteration count are zero, 253 // the active duration is zero. 254 assert_equals(effect.getComputedTiming().activeDuration, 0, 255 'Initial value of activeDuration'); 256 }, 'activeDuration of an animation with zero iterations'); 257 258 259 // -------------------- 260 // localTime 261 // -------------------- 262 263 test(t => { 264 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 265 const effect = div.getAnimations()[0].effect; 266 assert_equals(effect.getComputedTiming().localTime, 0, 267 'Initial value of localTime'); 268 }, 'localTime of a new animation'); 269 270 test(t => { 271 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 272 const anim = div.getAnimations()[0]; 273 anim.currentTime = 5 * MS_PER_SEC; 274 assert_times_equal(anim.effect.getComputedTiming().localTime, 275 anim.currentTime, 276 'current localTime after setting currentTime'); 277 }, 'localTime of an animation is always equal to currentTime'); 278 279 promise_test(async t => { 280 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 281 282 const anim = div.getAnimations()[0]; 283 anim.playbackRate = 2; // 2 times faster 284 285 await anim.ready; 286 287 assert_times_equal(anim.effect.getComputedTiming().localTime, 288 anim.currentTime, 289 'localTime is equal to currentTime'); 290 291 await waitForFrame(); 292 293 assert_times_equal(anim.effect.getComputedTiming().localTime, 294 anim.currentTime, 295 'localTime is equal to currentTime'); 296 }, 'localTime reflects playbackRate immediately'); 297 298 test(t => { 299 const div = addDiv(t); 300 const effect = new KeyframeEffect(div, {left: ["0px", "100px"]}); 301 302 assert_equals(effect.getComputedTiming().localTime, null, 303 'localTime for orphaned effect'); 304 }, 'localTime of an AnimationEffect without an Animation'); 305 306 307 // -------------------- 308 // progress 309 // 310 // Note: Even though CSS animations have a default animation-timing-function of 311 // "ease", this only applies between keyframes (often referred to as the 312 // keyframe-level easing). The progress value returned by getComputedTiming(), 313 // however, only reflects effect-level easing and this defaults to "linear", 314 // even for CSS animations. 315 // -------------------- 316 317 test(t => { 318 const tests = [ 319 { fill: '', progress: [null, null] }, 320 { fill: 'none', progress: [null, null] }, 321 { fill: 'forwards', progress: [null, 1.0] }, 322 { fill: 'backwards', progress: [0.0, null] }, 323 { fill: 'both', progress: [0.0, 1.0] }, 324 ]; 325 for (const test of tests) { 326 const div = addDiv(t, { 327 style: 'animation: moveAnimation 100s 10s ' + test.fill 328 }); 329 const anim = div.getAnimations()[0]; 330 assert_true(anim.effect.getComputedTiming().progress === test.progress[0], 331 `Initial progress with "${test.fill}" fill`); 332 anim.finish(); 333 assert_true(anim.effect.getComputedTiming().progress === test.progress[1], 334 `Initial progress with "${test.fill}" fill`); 335 } 336 }, 'progress of an animation with different fill modes'); 337 338 test(t => { 339 const div = addDiv(t, { style: 'animation: moveAnimation 10s 10 both' }); 340 const anim = div.getAnimations()[0]; 341 342 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 343 'Initial value of progress'); 344 anim.currentTime += 2.5 * MS_PER_SEC; 345 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 346 'Value of progress'); 347 anim.currentTime += 5 * MS_PER_SEC; 348 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 349 'Value of progress'); 350 anim.currentTime += 5 * MS_PER_SEC; 351 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 352 'Value of progress'); 353 anim.finish() 354 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 355 'Value of progress'); 356 }, 'progress of an integral repeating animation with normal direction'); 357 358 test(t => { 359 // Note: FillMode here is "both" because 360 // 1. Since this a zero-duration animation, it will already have finished 361 // so it won't be returned by getAnimations() unless it fills forwards. 362 // 2. Fill backwards, so the progress before phase wouldn't be 363 // unresolved (null value). 364 const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' }); 365 const anim = div.getAnimations()[0]; 366 367 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 368 'Initial value of progress in after phase'); 369 370 // Seek backwards 371 anim.currentTime -= 1 * MS_PER_SEC; 372 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 373 'Value of progress before phase'); 374 }, 'progress of an infinitely repeating zero-duration animation'); 375 376 test(t => { 377 // Default iterations = 1 378 const div = addDiv(t, { style: 'animation: moveAnimation 0s both' }); 379 const anim = div.getAnimations()[0]; 380 381 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 382 'Initial value of progress in after phase'); 383 384 // Seek backwards 385 anim.currentTime -= 1 * MS_PER_SEC; 386 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 387 'Value of progress before phase'); 388 }, 'progress of a finitely repeating zero-duration animation'); 389 390 test(t => { 391 const div = addDiv(t, { style: 'animation: moveAnimation 0s 5s 10.25 both' }); 392 const anim = div.getAnimations()[0]; 393 394 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 395 'Initial value of progress (before phase)'); 396 397 // Using iteration duration of 1 now. 398 // currentIteration now is floor(10.25) = 10, so progress should be 25%. 399 anim.finish(); 400 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 401 'Value of progress in after phase'); 402 }, 'progress of a non-integral repeating zero-duration animation'); 403 404 test(t => { 405 const div = addDiv(t, { 406 style: 'animation: moveAnimation 0s 5s 10.25 both reverse', 407 }); 408 const anim = div.getAnimations()[0]; 409 410 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 411 'Initial value of progress (before phase)'); 412 413 // Seek forwards 414 anim.finish(); 415 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 416 'Value of progress in after phase'); 417 }, 'Progress of a non-integral repeating zero-duration animation ' + 418 'with reversing direction'); 419 420 test(t => { 421 const div = addDiv(t, { 422 style: 'animation: moveAnimation 10s 10.25 both alternate', 423 }); 424 const anim = div.getAnimations()[0]; 425 426 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 427 'Initial value of progress'); 428 anim.currentTime += 2.5 * MS_PER_SEC; 429 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 430 'Value of progress'); 431 anim.currentTime += 5 * MS_PER_SEC; 432 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 433 'Value of progress'); 434 anim.currentTime += 5 * MS_PER_SEC; 435 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 436 'Value of progress'); 437 anim.finish() 438 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 439 'Value of progress'); 440 }, 'progress of a non-integral repeating animation ' + 441 'with alternate direction'); 442 443 test(t => { 444 const div = addDiv(t, { 445 style: 'animation: moveAnimation 10s 10.25 both alternate-reverse', 446 }); 447 const anim = div.getAnimations()[0]; 448 449 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 450 'Initial value of progress'); 451 anim.currentTime += 2.5 * MS_PER_SEC; 452 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 453 'Value of progress'); 454 anim.currentTime += 5 * MS_PER_SEC; 455 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 456 'Value of progress'); 457 anim.currentTime += 5 * MS_PER_SEC; 458 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 459 'Value of progress'); 460 anim.finish() 461 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 462 'Value of progress'); 463 }, 'progress of a non-integral repeating animation ' + 464 'with alternate-reversing direction'); 465 466 test(t => { 467 const div = addDiv(t, { 468 style: 'animation: moveAnimation 0s 10.25 both alternate', 469 }); 470 const anim = div.getAnimations()[0]; 471 472 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 473 'Initial value of progress'); 474 anim.currentTime += 2.5 * MS_PER_SEC; 475 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 476 'Value of progress'); 477 anim.currentTime -= 5 * MS_PER_SEC; 478 assert_equals(anim.effect.getComputedTiming().progress, 0.0, 479 'Value of progress'); 480 anim.finish() 481 assert_equals(anim.effect.getComputedTiming().progress, 0.25, 482 'Value of progress'); 483 }, 'progress of a non-integral repeating zero-duration animation ' + 484 'with alternate direction'); 485 486 test(t => { 487 const div = addDiv(t, { 488 style: 'animation: moveAnimation 0s 10.25 both alternate-reverse', 489 }); 490 const anim = div.getAnimations()[0]; 491 492 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 493 'Initial value of progress'); 494 anim.currentTime += 2.5 * MS_PER_SEC; 495 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 496 'Value of progress'); 497 anim.currentTime -= 5 * MS_PER_SEC; 498 assert_equals(anim.effect.getComputedTiming().progress, 1.0, 499 'Value of progress'); 500 anim.finish() 501 assert_equals(anim.effect.getComputedTiming().progress, 0.75, 502 'Value of progress'); 503 }, 'progress of a non-integral repeating zero-duration animation ' + 504 'with alternate-reverse direction'); 505 506 507 // -------------------- 508 // currentIteration 509 // -------------------- 510 511 test(t => { 512 const div = addDiv(t, { style: 'animation: moveAnimation 100s 2s' }); 513 const effect = div.getAnimations()[0].effect; 514 assert_equals(effect.getComputedTiming().currentIteration, null, 515 'Initial value of currentIteration before phase'); 516 }, 'currentIteration of a new animation with no backwards fill is unresolved ' + 517 'in before phase'); 518 519 test(t => { 520 const div = addDiv(t, { style: 'animation: moveAnimation 100s' }); 521 const anim = div.getAnimations()[0]; 522 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 523 'Initial value of currentIteration'); 524 }, 'currentIteration of a new animation is zero'); 525 526 test(t => { 527 // Note: FillMode here is "both" because 528 // 1. Since this a zero-duration animation, it will already have finished 529 // so it won't be returned by getAnimations() unless it fills forwards. 530 // 2. Fill backwards, so the currentIteration (before phase) wouldn't be 531 // unresolved (null value). 532 const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' }); 533 const anim = div.getAnimations()[0]; 534 535 assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity, 536 'Initial value of currentIteration in after phase'); 537 538 // Seek backwards 539 anim.currentTime -= 2 * MS_PER_SEC; 540 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 541 'Value of currentIteration count during before phase'); 542 }, 'currentIteration of an infinitely repeating zero-duration animation'); 543 544 test(t => { 545 const div = addDiv(t, { style: 'animation: moveAnimation 0s 10.5 both' }); 546 const anim = div.getAnimations()[0]; 547 548 // Note: currentIteration = ceil(iteration start + iteration count) - 1 549 assert_equals(anim.effect.getComputedTiming().currentIteration, 10, 550 'Initial value of currentIteration'); 551 552 // Seek backwards 553 anim.currentTime -= 2 * MS_PER_SEC; 554 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 555 'Value of currentIteration count during before phase'); 556 }, 'currentIteration of a finitely repeating zero-duration animation'); 557 558 test(t => { 559 const div = addDiv(t, { 560 style: 'animation: moveAnimation 100s 5.5 forwards' 561 }); 562 const anim = div.getAnimations()[0]; 563 564 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 565 'Initial value of currentIteration'); 566 // The 3rd iteration 567 // Note: currentIteration = floor(scaled active time / iteration duration) 568 anim.currentTime = 250 * MS_PER_SEC; 569 assert_equals(anim.effect.getComputedTiming().currentIteration, 2, 570 'Value of currentIteration during the 3rd iteration'); 571 // Finish 572 anim.finish(); 573 assert_equals(anim.effect.getComputedTiming().currentIteration, 5, 574 'Value of currentIteration in after phase'); 575 }, 'currentIteration of an animation with a non-integral iteration count'); 576 577 test(t => { 578 const div = addDiv(t, { style: 'animation: moveAnimation 100s 2 forwards' }); 579 const anim = div.getAnimations()[0]; 580 581 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 582 'Initial value of currentIteration'); 583 // Finish 584 anim.finish(); 585 assert_equals(anim.effect.getComputedTiming().currentIteration, 1, 586 'Value of currentIteration in after phase'); 587 }, 'currentIteration of an animation with an integral iteration count'); 588 589 test(t => { 590 const div = addDiv(t, { style: 'animation: moveAnimation 100s forwards' }); 591 const anim = div.getAnimations()[0]; 592 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 593 'Initial value of currentIteration'); 594 // Finish 595 anim.finish(); 596 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 597 'Value of currentIteration in after phase'); 598 }, 'currentIteration of an animation with a default iteration count'); 599 600 test(t => { 601 const div = addDiv(t); 602 const effect = new KeyframeEffect(div, {left: ["0px", "100px"]}); 603 604 assert_equals(effect.getComputedTiming().currentIteration, null, 605 'currentIteration for orphaned effect'); 606 }, 'currentIteration of an AnimationEffect without an Animation'); 607 608 // TODO: If iteration duration is Infinity, currentIteration is 0. 609 // However, we cannot set iteration duration to Infinity in CSS Animation now. 610 611 </script> 612 </body>