getAnimations.html (12692B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <title>Animatable.getAnimations</title> 4 <link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animatable-getanimations"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="../../testcommon.js"></script> 8 <body> 9 <script> 10 'use strict'; 11 12 test(t => { 13 const div = createDiv(t); 14 assert_array_equals(div.getAnimations(), []); 15 }, 'Returns an empty array for an element with no animations'); 16 17 test(t => { 18 const div = createDiv(t); 19 const animationA = div.animate(null, 100 * MS_PER_SEC); 20 const animationB = div.animate(null, 100 * MS_PER_SEC); 21 assert_array_equals(div.getAnimations(), [animationA, animationB]); 22 }, 'Returns both animations for an element with two animations'); 23 24 test(t => { 25 const divA = createDiv(t); 26 const divB = createDiv(t); 27 const animationA = divA.animate(null, 100 * MS_PER_SEC); 28 const animationB = divB.animate(null, 100 * MS_PER_SEC); 29 assert_array_equals(divA.getAnimations(), [animationA], 'divA'); 30 assert_array_equals(divB.getAnimations(), [animationB], 'divB'); 31 }, 'Returns only the animations specific to each sibling element'); 32 33 test(t => { 34 const divParent = createDiv(t); 35 const divChild = createDiv(t); 36 divParent.appendChild(divChild); 37 const animationParent = divParent.animate(null, 100 * MS_PER_SEC); 38 const animationChild = divChild.animate(null, 100 * MS_PER_SEC); 39 assert_array_equals(divParent.getAnimations(), [animationParent], 40 'divParent'); 41 assert_array_equals(divChild.getAnimations(), [animationChild], 'divChild'); 42 }, 'Returns only the animations specific to each parent/child element'); 43 44 test(t => { 45 const divParent = createDiv(t); 46 const divChild = createDiv(t); 47 divParent.appendChild(divChild); 48 const divGrandChildA = createDiv(t); 49 const divGrandChildB = createDiv(t); 50 divChild.appendChild(divGrandChildA); 51 divChild.appendChild(divGrandChildB); 52 53 // Trigger the animations in a somewhat random order 54 const animGrandChildB = divGrandChildB.animate(null, 100 * MS_PER_SEC); 55 const animChild = divChild.animate(null, 100 * MS_PER_SEC); 56 const animGrandChildA = divGrandChildA.animate(null, 100 * MS_PER_SEC); 57 58 assert_array_equals( 59 divParent.getAnimations({ subtree: true }), 60 [animGrandChildB, animChild, animGrandChildA], 61 'Returns expected animations from parent' 62 ); 63 assert_array_equals( 64 divChild.getAnimations({ subtree: true }), 65 [animGrandChildB, animChild, animGrandChildA], 66 'Returns expected animations from child' 67 ); 68 assert_array_equals( 69 divGrandChildA.getAnimations({ subtree: true }), 70 [animGrandChildA], 71 'Returns expected animations from grandchild A' 72 ); 73 }, 'Returns animations on descendants when subtree: true is specified'); 74 75 test(t => { 76 createStyle(t, { 77 '@keyframes anim': '', 78 [`.pseudo::before`]: 'animation: anim 100s; ' + "content: '';", 79 }); 80 const div = createDiv(t); 81 div.classList.add('pseudo'); 82 83 assert_equals( 84 div.getAnimations().length, 85 0, 86 'Returns no animations when subtree is false' 87 ); 88 assert_equals( 89 div.getAnimations({ subtree: true }).length, 90 1, 91 'Returns one animation when subtree is true' 92 ); 93 }, 'Returns animations on pseudo-elements when subtree: true is specified'); 94 95 test(t => { 96 const host = createDiv(t); 97 const shadow = host.attachShadow({ mode: 'open' }); 98 99 const elem = createDiv(t); 100 shadow.appendChild(elem); 101 102 const elemChild = createDiv(t); 103 elem.appendChild(elemChild); 104 105 elemChild.animate(null, 100 * MS_PER_SEC); 106 107 assert_equals( 108 host.getAnimations({ subtree: true }).length, 109 0, 110 'Returns no animations with subtree:true when called on the host' 111 ); 112 assert_equals( 113 elem.getAnimations({ subtree: true }).length, 114 1, 115 'Returns one animation when called on a parent in the shadow tree' 116 ); 117 }, 'Does NOT cross shadow-tree boundaries when subtree: true is specified'); 118 119 test(t => { 120 const foreignElement 121 = document.createElementNS('http://example.org/test', 'test'); 122 document.body.appendChild(foreignElement); 123 t.add_cleanup(() => { 124 foreignElement.remove(); 125 }); 126 127 const animation = foreignElement.animate(null, 100 * MS_PER_SEC); 128 assert_array_equals(foreignElement.getAnimations(), [animation]); 129 }, 'Returns animations for a foreign element'); 130 131 test(t => { 132 const div = createDiv(t); 133 const animation = div.animate(null, 100 * MS_PER_SEC); 134 animation.finish(); 135 assert_array_equals(div.getAnimations(), []); 136 }, 'Does not return finished animations that do not fill forwards'); 137 138 test(t => { 139 const div = createDiv(t); 140 const animation = div.animate(null, { 141 duration: 100 * MS_PER_SEC, 142 fill: 'forwards', 143 }); 144 animation.finish(); 145 assert_array_equals(div.getAnimations(), [animation]); 146 }, 'Returns finished animations that fill forwards'); 147 148 test(t => { 149 const div = createDiv(t); 150 const animation = div.animate(null, { 151 duration: 100 * MS_PER_SEC, 152 delay: 100 * MS_PER_SEC, 153 }); 154 assert_array_equals(div.getAnimations(), [animation]); 155 }, 'Returns animations yet to reach their active phase'); 156 157 test(t => { 158 const div = createDiv(t); 159 const animation = div.animate(null, 100 * MS_PER_SEC); 160 animation.playbackRate = -1; 161 assert_array_equals(div.getAnimations(), []); 162 }, 'Does not return reversed finished animations that do not fill backwards'); 163 164 test(t => { 165 const div = createDiv(t); 166 const animation = div.animate(null, { 167 duration: 100 * MS_PER_SEC, 168 fill: 'backwards', 169 }); 170 animation.playbackRate = -1; 171 assert_array_equals(div.getAnimations(), [animation]); 172 }, 'Returns reversed finished animations that fill backwards'); 173 174 test(t => { 175 const div = createDiv(t); 176 const animation = div.animate(null, 100 * MS_PER_SEC); 177 animation.playbackRate = -1; 178 animation.currentTime = 200 * MS_PER_SEC; 179 assert_array_equals(div.getAnimations(), [animation]); 180 }, 'Returns reversed animations yet to reach their active phase'); 181 182 test(t => { 183 const div = createDiv(t); 184 const animation = div.animate(null, { 185 duration: 100 * MS_PER_SEC, 186 delay: 100 * MS_PER_SEC, 187 }); 188 animation.playbackRate = 0; 189 assert_array_equals(div.getAnimations(), []); 190 }, 'Does not return animations with zero playback rate in before phase'); 191 192 test(t => { 193 const div = createDiv(t); 194 const animation = div.animate(null, 100 * MS_PER_SEC); 195 animation.finish(); 196 animation.playbackRate = 0; 197 animation.currentTime = 200 * MS_PER_SEC; 198 assert_array_equals(div.getAnimations(), []); 199 }, 'Does not return animations with zero playback rate in after phase'); 200 201 test(t => { 202 const div = createDiv(t); 203 const effect = new KeyframeEffect(div, {}, 225); 204 const animation = new Animation(effect, new DocumentTimeline()); 205 animation.reverse(); 206 animation.pause(); 207 animation.playbackRate = -1;; 208 animation.updatePlaybackRate(1); 209 assert_array_equals(div.getAnimations(), []); 210 }, 'Does not return an animation that has recently been made not current by setting the playback rate'); 211 212 test(t => { 213 const div = createDiv(t); 214 const animation = div.animate(null, 100 * MS_PER_SEC); 215 216 animation.finish(); 217 assert_array_equals(div.getAnimations(), [], 218 'Animation should not be returned when it is finished'); 219 220 animation.effect.updateTiming({ 221 duration: animation.effect.getTiming().duration + 100 * MS_PER_SEC, 222 }); 223 assert_array_equals(div.getAnimations(), [animation], 224 'Animation should be returned after extending the' 225 + ' duration'); 226 227 animation.effect.updateTiming({ duration: 0 }); 228 assert_array_equals(div.getAnimations(), [], 229 'Animation should not be returned after setting the' 230 + ' duration to zero'); 231 }, 'Returns animations based on dynamic changes to individual' 232 + ' animations\' duration'); 233 234 test(t => { 235 const div = createDiv(t); 236 const animation = div.animate(null, 100 * MS_PER_SEC); 237 238 animation.effect.updateTiming({ endDelay: -200 * MS_PER_SEC }); 239 assert_array_equals(div.getAnimations(), [], 240 'Animation should not be returned after setting a' 241 + ' negative end delay such that the end time is less' 242 + ' than the current time'); 243 244 animation.effect.updateTiming({ endDelay: 100 * MS_PER_SEC }); 245 assert_array_equals(div.getAnimations(), [animation], 246 'Animation should be returned after setting a positive' 247 + ' end delay such that the end time is more than the' 248 + ' current time'); 249 }, 'Returns animations based on dynamic changes to individual' 250 + ' animations\' end delay'); 251 252 test(t => { 253 const div = createDiv(t); 254 const animation = div.animate(null, 100 * MS_PER_SEC); 255 256 animation.finish(); 257 assert_array_equals(div.getAnimations(), [], 258 'Animation should not be returned when it is finished'); 259 260 animation.effect.updateTiming({ iterations: 10 }); 261 assert_array_equals(div.getAnimations(), [animation], 262 'Animation should be returned after inreasing the' 263 + ' number of iterations'); 264 265 animation.effect.updateTiming({ iterations: 0 }); 266 assert_array_equals(div.getAnimations(), [], 267 'Animations should not be returned after setting the' 268 + ' iteration count to zero'); 269 270 animation.effect.updateTiming({ iterations: Infinity }); 271 assert_array_equals(div.getAnimations(), [animation], 272 'Animation should be returned after inreasing the' 273 + ' number of iterations to infinity'); 274 }, 'Returns animations based on dynamic changes to individual' 275 + ' animations\' iteration count'); 276 277 test(t => { 278 const div = createDiv(t); 279 const animation = div.animate(null, 280 { duration: 100 * MS_PER_SEC, 281 delay: 50 * MS_PER_SEC, 282 endDelay: -50 * MS_PER_SEC }); 283 284 assert_array_equals(div.getAnimations(), [animation], 285 'Animation should be returned at during delay phase'); 286 287 animation.currentTime = 50 * MS_PER_SEC; 288 assert_array_equals(div.getAnimations(), [animation], 289 'Animation should be returned after seeking to the start' 290 + ' of the active interval'); 291 292 animation.currentTime = 100 * MS_PER_SEC; 293 assert_array_equals(div.getAnimations(), [], 294 'Animation should not be returned after seeking to the' 295 + ' clipped end of the active interval'); 296 }, 'Returns animations based on dynamic changes to individual' 297 + ' animations\' current time'); 298 299 promise_test(async t => { 300 const div = createDiv(t); 301 302 const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); 303 const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); 304 await animA.finished; 305 // It is not guaranteed that the mircrotask PerformCheckpoint() happens before 306 // the animation finish promised got resolved, because the microtask 307 // checkpoint could also be triggered from other source such as the event_loop 308 // Thus we wait for one animation frame to make sure the finished animation is 309 // properly removed. 310 await waitForNextFrame(1); 311 assert_array_equals(div.getAnimations(), [animB]); 312 }, 'Does not return an animation that has been removed'); 313 314 promise_test(async t => { 315 const div = createDiv(t); 316 317 const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); 318 const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' }); 319 await animA.finished; 320 321 animA.persist(); 322 323 assert_array_equals(div.getAnimations(), [animA, animB]); 324 }, 'Returns an animation that has been persisted'); 325 326 promise_test(async t => { 327 const div = createDiv(t); 328 const watcher = EventWatcher(t, div, 'transitionrun'); 329 330 // Create a covering animation to prevent transitions from firing after 331 // calling getAnimations(). 332 const coveringAnimation = new Animation( 333 new KeyframeEffect(div, { opacity: [0, 1] }, 100 * MS_PER_SEC) 334 ); 335 336 // Setup transition start point. 337 div.style.transition = 'opacity 100s'; 338 getComputedStyle(div).opacity; 339 340 // Update specified style but don't flush style. 341 div.style.opacity = '0.5'; 342 343 // Fetch animations 344 div.getAnimations(); 345 346 // Play the covering animation to ensure that only the call to 347 // getAnimations() has a chance to trigger transitions. 348 coveringAnimation.play(); 349 350 // If getAnimations() flushed style, we should get a transitionrun event. 351 await watcher.wait_for('transitionrun'); 352 }, 'Triggers a style change event'); 353 354 </script> 355 </body>