setting-the-start-time-of-an-animation.html (12942B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <title>Setting the start time of an animation</title> 4 <link rel="help" href="https://drafts.csswg.org/web-animations/#setting-the-start-time-of-an-animation"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="../../testcommon.js"></script> 8 <script src="../../resources/timing-override.js"></script> 9 <body> 10 <div id="log"></div> 11 <script> 12 'use strict'; 13 14 promise_test(async t => { 15 const animation = 16 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 17 null); 18 19 assert_throws_js(TypeError, () => { 20 animation.startTime = CSSNumericValue.parse("30%"); 21 }); 22 assert_throws_js(TypeError, () => { 23 animation.startTime = CSSNumericValue.parse("30deg"); 24 }); 25 26 animation.startTime = 2000; 27 assert_equals(animation.startTime, 2000, "Set start time using double"); 28 29 animation.startTime = CSSNumericValue.parse("3000"); 30 assert_equals(animation.startTime, 3000, "Set start time using " + 31 "CSSNumericValue number value"); 32 33 animation.startTime = CSSNumericValue.parse("4000ms"); 34 assert_equals(animation.startTime, 4000, "Set start time using " + 35 "CSSNumericValue milliseconds value"); 36 37 animation.startTime = CSSNumericValue.parse("50s"); 38 assert_equals(animation.startTime, 50000, "Set start time using " + 39 "CSSNumericValue seconds value"); 40 }, 'Validate different value types that can be used to set start time'); 41 42 test(t => { 43 // It should only be possible to set *either* the start time or the current 44 // time for an animation that does not have an active timeline. 45 46 const animation = 47 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 48 null); 49 50 assert_equals(animation.currentTime, null, 'Intial current time'); 51 assert_equals(animation.startTime, null, 'Intial start time'); 52 53 animation.currentTime = 1000; 54 assert_equals(animation.currentTime, 1000, 55 'Setting the current time succeeds'); 56 assert_equals(animation.startTime, null, 57 'Start time remains null after setting current time'); 58 59 animation.startTime = 1000; 60 assert_equals(animation.startTime, 1000, 61 'Setting the start time succeeds'); 62 assert_equals(animation.currentTime, null, 63 'Setting the start time clears the current time'); 64 65 animation.startTime = null; 66 assert_equals(animation.startTime, null, 67 'Setting the start time to an unresolved time succeeds'); 68 assert_equals(animation.currentTime, null, 'The current time is unaffected'); 69 70 }, 'Setting the start time of an animation without an active timeline'); 71 72 test(t => { 73 // Setting an unresolved start time on an animation without an active 74 // timeline should not clear the current time. 75 76 const animation = 77 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 78 null); 79 80 assert_equals(animation.currentTime, null, 'Intial current time'); 81 assert_equals(animation.startTime, null, 'Intial start time'); 82 83 animation.currentTime = 1000; 84 assert_equals(animation.currentTime, 1000, 85 'Setting the current time succeeds'); 86 assert_equals(animation.startTime, null, 87 'Start time remains null after setting current time'); 88 89 animation.startTime = null; 90 assert_equals(animation.startTime, null, 'Start time remains unresolved'); 91 assert_equals(animation.currentTime, 1000, 'Current time is unaffected'); 92 93 }, 'Setting an unresolved start time an animation without an active timeline' 94 + ' does not clear the current time'); 95 96 test(t => { 97 const animation = 98 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 99 document.timeline); 100 101 // So long as a hold time is set, querying the current time will return 102 // the hold time. 103 104 // Since the start time is unresolved at this point, setting the current time 105 // will set the hold time 106 animation.currentTime = 1000; 107 assert_equals(animation.currentTime, 1000, 108 'The current time is calculated from the hold time'); 109 110 // If we set the start time, however, we should clear the hold time. 111 animation.startTime = document.timeline.currentTime - 2000; 112 assert_time_equals_literal(animation.currentTime, 2000, 113 'The current time is calculated from the start' 114 + ' time, not the hold time'); 115 116 // Sanity check 117 assert_equals(animation.playState, 'running', 118 'Animation reports it is running after setting a resolved' 119 + ' start time'); 120 }, 'Setting the start time clears the hold time'); 121 122 test(t => { 123 const animation = 124 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 125 document.timeline); 126 127 // Set up a running animation (i.e. both start time and current time 128 // are resolved). 129 animation.startTime = document.timeline.currentTime - 1000; 130 assert_equals(animation.playState, 'running'); 131 assert_time_equals_literal(animation.currentTime, 1000, 132 'Current time is resolved for a running animation'); 133 134 // Clear start time 135 animation.startTime = null; 136 assert_time_equals_literal(animation.currentTime, 1000, 137 'Hold time is set after start time is made' 138 + ' unresolved'); 139 assert_equals(animation.playState, 'paused', 140 'Animation reports it is paused after setting an unresolved' 141 + ' start time'); 142 }, 'Setting an unresolved start time sets the hold time'); 143 144 promise_test(async t => { 145 const animation = 146 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 147 document.timeline); 148 149 let readyPromiseCallbackCalled = false; 150 animation.ready.then(() => { readyPromiseCallbackCalled = true; } ); 151 152 // Put the animation in the play-pending state 153 animation.play(); 154 155 // Sanity check 156 assert_true(animation.pending && animation.playState === 'running', 157 'Animation is in play-pending state'); 158 159 // Setting the start time should resolve the 'ready' promise, i.e. 160 // it should schedule a microtask to run the promise callbacks. 161 animation.startTime = document.timeline.currentTime; 162 assert_false(readyPromiseCallbackCalled, 163 'Ready promise callback is not called synchronously'); 164 165 // If we schedule another microtask then it should run immediately after 166 // the ready promise resolution microtask. 167 await Promise.resolve(); 168 assert_true(readyPromiseCallbackCalled, 169 'Ready promise callback called after setting startTime'); 170 }, 'Setting the start time resolves a pending ready promise'); 171 172 promise_test(async t => { 173 const animation = 174 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 175 document.timeline); 176 177 let readyPromiseCallbackCalled = false; 178 animation.ready.then(() => { readyPromiseCallbackCalled = true; } ); 179 180 // Put the animation in the pause-pending state 181 animation.startTime = document.timeline.currentTime; 182 animation.pause(); 183 184 // Sanity check 185 assert_true(animation.pending && animation.playState === 'paused', 186 'Animation is in pause-pending state'); 187 188 // Setting the start time should resolve the 'ready' promise although 189 // the resolution callbacks when be run in a separate microtask. 190 animation.startTime = null; 191 assert_false(readyPromiseCallbackCalled, 192 'Ready promise callback is not called synchronously'); 193 194 await Promise.resolve(); 195 assert_true(readyPromiseCallbackCalled, 196 'Ready promise callback called after setting startTime'); 197 }, 'Setting the start time resolves a pending pause task'); 198 199 promise_test(async t => { 200 const animation = 201 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 202 document.timeline); 203 204 // Put the animation in the play-pending state 205 animation.play(); 206 207 // Sanity check 208 assert_true(animation.pending, 'Animation is pending'); 209 assert_equals(animation.playState, 'running', 210 'Animation is play-pending'); 211 assert_equals(animation.startTime, null, 'Start time is null'); 212 213 // Even though the startTime is already null, setting it to the same value 214 // should still cancel the pending task. 215 animation.startTime = null; 216 assert_false(animation.pending, 'Animation is no longer pending'); 217 assert_equals(animation.playState, 'paused', 'Animation is paused'); 218 }, 'Setting an unresolved start time on a play-pending animation makes it' 219 + ' paused'); 220 221 promise_test(async t => { 222 const animation = 223 new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC), 224 document.timeline); 225 226 // Set start time such that the current time is past the end time 227 animation.startTime = document.timeline.currentTime 228 - 110 * MS_PER_SEC; 229 assert_equals(animation.playState, 'finished', 230 'Seeked to finished state using the startTime'); 231 232 // If the 'did seek' flag is true, the current time should be greater than 233 // the effect end. 234 assert_greater_than(animation.currentTime, 235 animation.effect.getComputedTiming().endTime, 236 'Setting the start time updated the finished state with' 237 + ' the \'did seek\' flag set to true'); 238 239 // Furthermore, that time should persist if we have correctly updated 240 // the hold time 241 const finishedCurrentTime = animation.currentTime; 242 await waitForAnimationFrames(1); 243 assert_equals(animation.currentTime, finishedCurrentTime, 244 'Current time does not change after seeking past the effect' 245 + ' end time by setting the current time'); 246 }, 'Setting the start time updates the finished state'); 247 248 promise_test(async t => { 249 const anim = createDiv(t).animate(null, 100 * MS_PER_SEC); 250 251 // We should be play-pending now 252 assert_true(anim.pending); 253 assert_equals(anim.playState, 'running'); 254 255 // Apply a pending playback rate 256 anim.updatePlaybackRate(2); 257 assert_equals(anim.playbackRate, 1); 258 assert_true(anim.pending); 259 260 // Setting the start time should apply the pending playback rate 261 anim.startTime = anim.timeline.currentTime - 25 * MS_PER_SEC; 262 assert_equals(anim.playbackRate, 2); 263 assert_false(anim.pending); 264 265 // Sanity check that the start time is preserved and current time is 266 // calculated using the new playback rate 267 assert_times_equal(anim.startTime, 268 anim.timeline.currentTime - 25 * MS_PER_SEC); 269 assert_time_equals_literal(anim.currentTime, 50 * MS_PER_SEC); 270 }, 'Setting the start time of a play-pending animation applies a pending playback rate'); 271 272 promise_test(async t => { 273 const anim = createDiv(t).animate(null, 100 * MS_PER_SEC); 274 await anim.ready; 275 276 // We should be running now 277 assert_false(anim.pending); 278 assert_equals(anim.playState, 'running'); 279 280 // Apply a pending playback rate 281 anim.updatePlaybackRate(2); 282 assert_equals(anim.playbackRate, 1); 283 assert_true(anim.pending); 284 285 // Setting the start time should apply the pending playback rate 286 anim.startTime = anim.timeline.currentTime - 25 * MS_PER_SEC; 287 assert_equals(anim.playbackRate, 2); 288 assert_false(anim.pending); 289 290 // Sanity check that the start time is preserved and current time is 291 // calculated using the new playback rate 292 assert_times_equal(anim.startTime, 293 anim.timeline.currentTime - 25 * MS_PER_SEC); 294 assert_time_equals_literal(parseInt(anim.currentTime.toPrecision(5), 10), 50 * MS_PER_SEC); 295 }, 'Setting the start time of a playing animation applies a pending playback rate'); 296 297 promise_test(async t => { 298 const anim = createDiv(t).animate(null, 100 * MS_PER_SEC); 299 await anim.ready; 300 assert_equals(anim.playState, 'running'); 301 302 // Setting the start time updates the finished state. The hold time is not 303 // constrained by the effect end time. 304 anim.startTime = -200 * MS_PER_SEC; 305 assert_equals(anim.playState, 'finished'); 306 307 assert_times_equal(anim.currentTime, 308 document.timeline.currentTime + 200 * MS_PER_SEC); 309 }, 'Setting the start time on a running animation updates the play state'); 310 311 promise_test(async t => { 312 const anim = createDiv(t).animate(null, 100 * MS_PER_SEC); 313 await anim.ready; 314 315 // Setting the start time updates the finished state. The hold time is not 316 // constrained by the normal range of the animation time. 317 anim.currentTime = 100 * MS_PER_SEC; 318 assert_equals(anim.playState, 'finished'); 319 anim.playbackRate = -1; 320 assert_equals(anim.playState, 'running'); 321 anim.startTime = -200 * MS_PER_SEC; 322 assert_equals(anim.playState, 'finished'); 323 assert_times_equal(anim.currentTime, 324 -document.timeline.currentTime - 200 * MS_PER_SEC); 325 }, 'Setting the start time on a reverse running animation updates the play ' 326 + 'state'); 327 </script> 328 </body>