updateTiming.html (19054B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>AnimationEffect.updateTiming</title> 4 <link rel="help" href="https://drafts.csswg.org/web-animations-1/#dom-animationeffect-updatetiming"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="../../testcommon.js"></script> 8 <script src="../../resources/easing-tests.js"></script> 9 <script src="../../resources/timing-tests.js"></script> 10 <body> 11 <div id="log"></div> 12 <script> 13 'use strict'; 14 15 // ------------------------------ 16 // delay 17 // ------------------------------ 18 19 test(t => { 20 const anim = createDiv(t).animate(null, 100); 21 anim.effect.updateTiming({ delay: 100 }); 22 assert_equals(anim.effect.getTiming().delay, 100, 'set delay 100'); 23 assert_equals(anim.effect.getComputedTiming().delay, 100, 24 'getComputedTiming() after set delay 100'); 25 }, 'Allows setting the delay to a positive number'); 26 27 test(t => { 28 const anim = createDiv(t).animate(null, 100); 29 anim.effect.updateTiming({ delay: -100 }); 30 assert_equals(anim.effect.getTiming().delay, -100, 'set delay -100'); 31 assert_equals(anim.effect.getComputedTiming().delay, -100, 32 'getComputedTiming() after set delay -100'); 33 }, 'Allows setting the delay to a negative number'); 34 35 test(t => { 36 const anim = createDiv(t).animate(null, 100); 37 anim.effect.updateTiming({ delay: 100 }); 38 assert_equals(anim.effect.getComputedTiming().progress, null); 39 assert_equals(anim.effect.getComputedTiming().currentIteration, null); 40 }, 'Allows setting the delay of an animation in progress: positive delay that' 41 + ' causes the animation to be no longer in-effect'); 42 43 test(t => { 44 const anim = createDiv(t).animate(null, { fill: 'both', duration: 100 }); 45 anim.effect.updateTiming({ delay: -50 }); 46 assert_equals(anim.effect.getComputedTiming().progress, 0.5); 47 }, 'Allows setting the delay of an animation in progress: negative delay that' 48 + ' seeks into the active interval'); 49 50 test(t => { 51 const anim = createDiv(t).animate(null, { fill: 'both', duration: 100 }); 52 anim.effect.updateTiming({ delay: -100 }); 53 assert_equals(anim.effect.getComputedTiming().progress, 1); 54 assert_equals(anim.effect.getComputedTiming().currentIteration, 0); 55 }, 'Allows setting the delay of an animation in progress: large negative delay' 56 + ' that causes the animation to be finished'); 57 58 for (const invalid of gBadDelayValues) { 59 test(t => { 60 const anim = createDiv(t).animate(null); 61 assert_throws_js(TypeError, () => { 62 anim.effect.updateTiming({ delay: invalid }); 63 }); 64 }, `Throws when setting invalid delay value: ${invalid}`); 65 } 66 67 68 // ------------------------------ 69 // endDelay 70 // ------------------------------ 71 72 test(t => { 73 const anim = createDiv(t).animate(null, 2000); 74 anim.effect.updateTiming({ endDelay: 123.45 }); 75 assert_time_equals_literal(anim.effect.getTiming().endDelay, 123.45, 76 'set endDelay 123.45'); 77 assert_time_equals_literal(anim.effect.getComputedTiming().endDelay, 123.45, 78 'getComputedTiming() after set endDelay 123.45'); 79 }, 'Allows setting the endDelay to a positive number'); 80 81 test(t => { 82 const anim = createDiv(t).animate(null, 2000); 83 anim.effect.updateTiming({ endDelay: -1000 }); 84 assert_equals(anim.effect.getTiming().endDelay, -1000, 'set endDelay -1000'); 85 assert_equals(anim.effect.getComputedTiming().endDelay, -1000, 86 'getComputedTiming() after set endDelay -1000'); 87 }, 'Allows setting the endDelay to a negative number'); 88 89 test(t => { 90 const anim = createDiv(t).animate(null, 2000); 91 assert_throws_js(TypeError, () => { 92 anim.effect.updateTiming({ endDelay: Infinity }); 93 }); 94 }, 'Throws when setting the endDelay to infinity'); 95 96 test(t => { 97 const anim = createDiv(t).animate(null, 2000); 98 assert_throws_js(TypeError, () => { 99 anim.effect.updateTiming({ endDelay: -Infinity }); 100 }); 101 }, 'Throws when setting the endDelay to negative infinity'); 102 103 104 // ------------------------------ 105 // fill 106 // ------------------------------ 107 108 for (const fill of ['none', 'forwards', 'backwards', 'both']) { 109 test(t => { 110 const anim = createDiv(t).animate(null, 100); 111 anim.effect.updateTiming({ fill }); 112 assert_equals(anim.effect.getTiming().fill, fill, 'set fill ' + fill); 113 assert_equals(anim.effect.getComputedTiming().fill, fill, 114 'getComputedTiming() after set fill ' + fill); 115 }, `Allows setting the fill to '${fill}'`); 116 } 117 118 119 // ------------------------------ 120 // iterationStart 121 // ------------------------------ 122 123 test(t => { 124 const anim = createDiv(t).animate(null, 125 { iterationStart: 0.2, 126 iterations: 1, 127 fill: 'both', 128 duration: 100, 129 delay: 1 }); 130 anim.effect.updateTiming({ iterationStart: 2.5 }); 131 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); 132 assert_equals(anim.effect.getComputedTiming().currentIteration, 2); 133 }, 'Allows setting the iterationStart of an animation in progress:' 134 + ' backwards-filling'); 135 136 test(t => { 137 const anim = createDiv(t).animate(null, 138 { iterationStart: 0.2, 139 iterations: 1, 140 fill: 'both', 141 duration: 100, 142 delay: 0 }); 143 anim.effect.updateTiming({ iterationStart: 2.5 }); 144 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); 145 assert_equals(anim.effect.getComputedTiming().currentIteration, 2); 146 }, 'Allows setting the iterationStart of an animation in progress:' 147 + ' active phase'); 148 149 test(t => { 150 const anim = createDiv(t).animate(null, 151 { iterationStart: 0.2, 152 iterations: 1, 153 fill: 'both', 154 duration: 100, 155 delay: 0 }); 156 anim.finish(); 157 anim.effect.updateTiming({ iterationStart: 2.5 }); 158 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5); 159 assert_equals(anim.effect.getComputedTiming().currentIteration, 3); 160 }, 'Allows setting the iterationStart of an animation in progress:' 161 + ' forwards-filling'); 162 163 for (const invalid of gBadIterationStartValues) { 164 test(t => { 165 const anim = createDiv(t).animate(null); 166 assert_throws_js(TypeError, () => { 167 anim.effect.updateTiming({ iterationStart: invalid }); 168 }, `setting ${invalid}`); 169 }, `Throws when setting invalid iterationStart value: ${invalid}`); 170 } 171 172 // ------------------------------ 173 // iterations 174 // ------------------------------ 175 176 test(t => { 177 const anim = createDiv(t).animate(null, 2000); 178 anim.effect.updateTiming({ iterations: 2 }); 179 assert_equals(anim.effect.getTiming().iterations, 2, 'set duration 2'); 180 assert_equals(anim.effect.getComputedTiming().iterations, 2, 181 'getComputedTiming() after set iterations 2'); 182 }, 'Allows setting iterations to a double value'); 183 184 test(t => { 185 const anim = createDiv(t).animate(null, 2000); 186 anim.effect.updateTiming({ iterations: Infinity }); 187 assert_equals(anim.effect.getTiming().iterations, Infinity, 188 'set duration Infinity'); 189 assert_equals(anim.effect.getComputedTiming().iterations, Infinity, 190 'getComputedTiming() after set iterations Infinity'); 191 }, 'Allows setting iterations to infinity'); 192 193 for (const invalid of gBadIterationsValues) { 194 test(t => { 195 const anim = createDiv(t).animate(null); 196 assert_throws_js(TypeError, () => { 197 anim.effect.updateTiming({ iterations: invalid }); 198 }); 199 }, `Throws when setting invalid iterations value: ${invalid}`); 200 } 201 202 test(t => { 203 const anim = createDiv(t).animate(null, { duration: 100000, fill: 'both' }); 204 205 anim.finish(); 206 207 assert_equals(anim.effect.getComputedTiming().progress, 1, 208 'progress when animation is finished'); 209 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 210 'current iteration when animation is finished'); 211 212 anim.effect.updateTiming({ iterations: 2 }); 213 214 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 215 0, 216 'progress after adding an iteration'); 217 assert_time_equals_literal(anim.effect.getComputedTiming().currentIteration, 218 1, 219 'current iteration after adding an iteration'); 220 221 anim.effect.updateTiming({ iterations: 0 }); 222 223 assert_equals(anim.effect.getComputedTiming().progress, 0, 224 'progress after setting iterations to zero'); 225 assert_equals(anim.effect.getComputedTiming().currentIteration, 0, 226 'current iteration after setting iterations to zero'); 227 228 anim.effect.updateTiming({ iterations: Infinity }); 229 230 assert_equals(anim.effect.getComputedTiming().progress, 0, 231 'progress after setting iterations to Infinity'); 232 assert_equals(anim.effect.getComputedTiming().currentIteration, 1, 233 'current iteration after setting iterations to Infinity'); 234 }, 'Allows setting the iterations of an animation in progress'); 235 236 237 // ------------------------------ 238 // duration 239 // ------------------------------ 240 241 for (const duration of gGoodDurationValues) { 242 test(t => { 243 const anim = createDiv(t).animate(null, 2000); 244 anim.effect.updateTiming({ duration: duration.specified }); 245 if (typeof duration.specified === 'number') { 246 assert_time_equals_literal(anim.effect.getTiming().duration, 247 duration.specified, 248 'Updates specified duration'); 249 } else { 250 assert_equals(anim.effect.getTiming().duration, duration.specified, 251 'Updates specified duration'); 252 } 253 assert_time_equals_literal(anim.effect.getComputedTiming().duration, 254 duration.computed, 255 'Updates computed duration'); 256 }, `Allows setting the duration to ${duration.specified}`); 257 } 258 259 for (const invalid of gBadDurationValues) { 260 test(t => { 261 assert_throws_js(TypeError, () => { 262 createDiv(t).animate(null, { duration: invalid }); 263 }); 264 }, 'Throws when setting invalid duration: ' 265 + (typeof invalid === 'string' ? `"${invalid}"` : invalid)); 266 } 267 268 test(t => { 269 const anim = createDiv(t).animate(null, { duration: 100000, fill: 'both' }); 270 anim.finish(); 271 assert_equals(anim.effect.getComputedTiming().progress, 1, 272 'progress when animation is finished'); 273 anim.effect.updateTiming({ duration: anim.effect.getTiming().duration * 2 }); 274 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.5, 275 'progress after doubling the duration'); 276 anim.effect.updateTiming({ duration: 0 }); 277 assert_equals(anim.effect.getComputedTiming().progress, 1, 278 'progress after setting duration to zero'); 279 anim.effect.updateTiming({ duration: 'auto' }); 280 assert_equals(anim.effect.getComputedTiming().progress, 1, 281 'progress after setting duration to \'auto\''); 282 }, 'Allows setting the duration of an animation in progress'); 283 284 promise_test(t => { 285 const anim = createDiv(t).animate(null, 100 * MS_PER_SEC); 286 return anim.ready.then(() => { 287 const originalStartTime = anim.startTime; 288 const originalCurrentTime = anim.currentTime; 289 assert_time_equals_literal( 290 anim.effect.getComputedTiming().duration, 291 100 * MS_PER_SEC, 292 'Initial duration should be as set on KeyframeEffect' 293 ); 294 295 anim.effect.updateTiming({ duration: 200 * MS_PER_SEC }); 296 assert_time_equals_literal( 297 anim.effect.getComputedTiming().duration, 298 200 * MS_PER_SEC, 299 'Effect duration should have been updated' 300 ); 301 assert_times_equal(anim.startTime, originalStartTime, 302 'startTime should be unaffected by changing effect ' + 303 'duration'); 304 assert_times_equal(anim.currentTime, originalCurrentTime, 305 'currentTime should be unaffected by changing effect ' + 306 'duration'); 307 }); 308 }, 'Allows setting the duration of an animation in progress such that the' + 309 ' the start and current time do not change'); 310 311 312 // ------------------------------ 313 // direction 314 // ------------------------------ 315 316 test(t => { 317 const anim = createDiv(t).animate(null, 2000); 318 319 const directions = ['normal', 'reverse', 'alternate', 'alternate-reverse']; 320 for (const direction of directions) { 321 anim.effect.updateTiming({ direction: direction }); 322 assert_equals(anim.effect.getTiming().direction, direction, 323 `set direction to ${direction}`); 324 } 325 }, 'Allows setting the direction to each of the possible keywords'); 326 327 test(t => { 328 const anim = createDiv(t).animate(null, { 329 duration: 10000, 330 direction: 'normal', 331 }); 332 anim.currentTime = 7000; 333 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, 334 'progress before updating direction'); 335 336 anim.effect.updateTiming({ direction: 'reverse' }); 337 338 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, 339 'progress after updating direction'); 340 }, 'Allows setting the direction of an animation in progress from \'normal\' to' 341 + ' \'reverse\''); 342 343 test(t => { 344 const anim = createDiv(t).animate(null, 345 { duration: 10000, direction: 'normal' }); 346 assert_equals(anim.effect.getComputedTiming().progress, 0, 347 'progress before updating direction'); 348 349 anim.effect.updateTiming({ direction: 'reverse' }); 350 351 assert_equals(anim.effect.getComputedTiming().progress, 1, 352 'progress after updating direction'); 353 }, 'Allows setting the direction of an animation in progress from \'normal\' to' 354 + ' \'reverse\' while at start of active interval'); 355 356 test(t => { 357 const anim = createDiv(t).animate(null, 358 { fill: 'backwards', 359 duration: 10000, 360 delay: 10000, 361 direction: 'normal' }); 362 assert_equals(anim.effect.getComputedTiming().progress, 0, 363 'progress before updating direction'); 364 365 anim.effect.updateTiming({ direction: 'reverse' }); 366 367 assert_equals(anim.effect.getComputedTiming().progress, 1, 368 'progress after updating direction'); 369 }, 'Allows setting the direction of an animation in progress from \'normal\' to' 370 + ' \'reverse\' while filling backwards'); 371 372 test(t => { 373 const anim = createDiv(t).animate(null, 374 { iterations: 2, 375 duration: 10000, 376 direction: 'normal' }); 377 anim.currentTime = 17000; 378 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, 379 'progress before updating direction'); 380 381 anim.effect.updateTiming({ direction: 'alternate' }); 382 383 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, 384 'progress after updating direction'); 385 }, 'Allows setting the direction of an animation in progress from \'normal\' to' 386 + ' \'alternate\''); 387 388 test(t => { 389 const anim = createDiv(t).animate(null, 390 { iterations: 2, 391 duration: 10000, 392 direction: 'alternate' }); 393 anim.currentTime = 17000; 394 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.3, 395 'progress before updating direction'); 396 397 anim.effect.updateTiming({ direction: 'alternate-reverse' }); 398 399 assert_time_equals_literal(anim.effect.getComputedTiming().progress, 0.7, 400 'progress after updating direction'); 401 }, 'Allows setting the direction of an animation in progress from \'alternate\'' 402 + ' to \'alternate-reverse\''); 403 404 405 // ------------------------------ 406 // easing 407 // ------------------------------ 408 409 function assert_progress(animation, currentTime, easingFunction) { 410 animation.currentTime = currentTime; 411 const portion = currentTime / animation.effect.getTiming().duration; 412 assert_approx_equals(animation.effect.getComputedTiming().progress, 413 easingFunction(portion), 414 0.01, 415 'The progress of the animation should be approximately' 416 + ` ${easingFunction(portion)} at ${currentTime}ms`); 417 } 418 419 for (const options of gEasingTests) { 420 test(t => { 421 const target = createDiv(t); 422 const anim = target.animate(null, 423 { duration: 1000 * MS_PER_SEC, 424 fill: 'forwards' }); 425 anim.effect.updateTiming({ easing: options.easing }); 426 assert_equals(anim.effect.getTiming().easing, 427 options.serialization || options.easing); 428 429 const easing = options.easingFunction; 430 assert_progress(anim, 0, easing); 431 assert_progress(anim, 250 * MS_PER_SEC, easing); 432 assert_progress(anim, 500 * MS_PER_SEC, easing); 433 assert_progress(anim, 750 * MS_PER_SEC, easing); 434 assert_progress(anim, 1000 * MS_PER_SEC, easing); 435 }, `Allows setting the easing to a ${options.desc}`); 436 } 437 438 for (const easing of gRoundtripEasings) { 439 test(t => { 440 const anim = createDiv(t).animate(null); 441 anim.effect.updateTiming({ easing: easing }); 442 assert_equals(anim.effect.getTiming().easing, easing); 443 }, `Updates the specified value when setting the easing to '${easing}'`); 444 } 445 446 test(t => { 447 const delay = 1000 * MS_PER_SEC; 448 449 const target = createDiv(t); 450 const anim = target.animate(null, 451 { duration: 1000 * MS_PER_SEC, 452 fill: 'both', 453 delay: delay, 454 easing: 'steps(2, start)' }); 455 456 anim.effect.updateTiming({ easing: 'steps(2, end)' }); 457 assert_equals(anim.effect.getComputedTiming().progress, 0, 458 'easing replace to steps(2, end) at before phase'); 459 460 anim.currentTime = delay + 750 * MS_PER_SEC; 461 assert_equals(anim.effect.getComputedTiming().progress, 0.5, 462 'change currentTime to active phase'); 463 464 anim.effect.updateTiming({ easing: 'steps(2, start)' }); 465 assert_equals(anim.effect.getComputedTiming().progress, 1, 466 'easing replace to steps(2, start) at active phase'); 467 468 anim.currentTime = delay + 1500 * MS_PER_SEC; 469 anim.effect.updateTiming({ easing: 'steps(2, end)' }); 470 assert_equals(anim.effect.getComputedTiming().progress, 1, 471 'easing replace to steps(2, end) again at after phase'); 472 }, 'Allows setting the easing of an animation in progress'); 473 474 </script> 475 </body>