animation-trigger-multiple-triggers.tentative.html (7971B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <link rel="help" src="https://drafts.csswg.org/css-animations-2/#animation-trigger"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/web-animations/testcommon.js"></script> 8 <script src="/dom/events/scrolling/scroll_support.js"></script> 9 <script src="support/support.js"></script> 10 </head> 11 <body> 12 <style> 13 .subject, .target { 14 height: 50px; 15 width: 50px; 16 background-color: red; 17 } 18 .scroller { 19 overflow-y: scroll; 20 height: 500px; 21 width: 500px; 22 border: solid 1px; 23 position: relative; 24 } 25 #space { 26 width: 50px; 27 height: 600px; 28 } 29 </style> 30 <div id="wrapper"> 31 <div id="scroller" class="scroller"> 32 <div id="space"></div> 33 <div id="subject1" class="subject"></div> 34 <div id="space"></div> 35 <div id="subject2" class="subject"></div> 36 <div id="space"></div> 37 </div> 38 <div id="target" class="target"></div> 39 </div> 40 <script> 41 // The trigger and exit ranges are the same for this test. 42 const TRIGGER_START_PX = 150; 43 const TRIGGER_END_PX = 200; 44 const scroller = document.getElementById("scroller"); 45 const subject1 = document.getElementById("subject1"); 46 const subject2 = document.getElementById("subject2"); 47 const target = document.getElementById("target"); 48 49 function getRangeBoundariesForSubject(subject, scroller) { 50 const cover_start_offset = subject.offsetTop - scroller.clientHeight; 51 return getRangeBoundariesForTest( 52 cover_start_offset + TRIGGER_START_PX, 53 cover_start_offset + TRIGGER_END_PX, 54 cover_start_offset + TRIGGER_START_PX, 55 cover_start_offset + TRIGGER_END_PX, 56 scroller); 57 } 58 const rangeBoundaries1 = getRangeBoundariesForSubject(subject1, 59 scroller); 60 const rangeBoundaries2 = getRangeBoundariesForSubject(subject2, 61 scroller); 62 63 const ANIMATION_DURATION_MS = 1; 64 const view_timeline1 = new ViewTimeline({ subject: subject1 }); 65 const view_timeline2 = new ViewTimeline({ subject: subject2 }); 66 function setupAnimation() { 67 const animation = new Animation( 68 new KeyframeEffect( 69 target, 70 [ 71 { transform: "scaleX(1)", backgroundColor: "pink" }, 72 { transform: "scaleX(5)", backgroundColor: "pink" } 73 ], 74 { duration: ANIMATION_DURATION_MS, fill: "both" } 75 )); 76 return animation; 77 } 78 function setupAnimationTrigger(view_timeline) { 79 const trigger = new TimelineTrigger({ 80 timeline: view_timeline, 81 rangeStart: `${TRIGGER_START_PX}px`, 82 rangeEnd: `${TRIGGER_END_PX}px` 83 }); 84 return trigger; 85 } 86 87 promise_test(async (test) => { 88 const animation = setupAnimation(); 89 const trigger1 = setupAnimationTrigger(view_timeline1); 90 const trigger2 = setupAnimationTrigger(view_timeline2); 91 92 // Test preconditions. 93 assert_equals(animation.playState, "idle", "animation is idle"); 94 assert_equals(animation.currentTime, null, "currentTime is null"); 95 assert_equals(scroller.scrollTop, 0, "scroller is not scrolled, i.e. " + 96 "not within the trigger range"); 97 assert_array_equals(trigger1.getAnimations(), [], 98 "trigger1 has no animation attached"); 99 assert_array_equals(trigger2.getAnimations(), [], 100 "trigger2 has no animation attached"); 101 102 trigger1.addAnimation(animation, "play-forwards", "reset"); 103 trigger2.addAnimation(animation, "play-forwards", "reset"); 104 105 // Test preconditions. 106 assert_equals(animation.playState, "paused", "animation is idle"); 107 assert_equals(animation.currentTime, 0, "currentTime is 0"); 108 assert_array_equals(trigger1.getAnimations(), [animation], 109 "trigger1 has animation attached"); 110 assert_array_equals(trigger2.getAnimations(), [animation], 111 "trigger2 has animation attached"); 112 113 await rangeBoundaries1.enterTriggerRange(); 114 await animation.finished; 115 assert_equals(animation.playState, "finished", 116 "animation is played by trigger1"); 117 assert_times_equal(animation.currentTime, ANIMATION_DURATION_MS, 118 `currentTime is ${ANIMATION_DURATION_MS}`); 119 120 await rangeBoundaries1.exitExitRangeBelow(); 121 await waitForNextFrame(); 122 assert_equals(animation.playState, "paused", 123 "animation should be reset by trigger1"); 124 assert_times_equal(animation.currentTime, 0, "currentTime is 0"); 125 126 // animation should be played by trigger2. 127 rangeBoundaries2.enterTriggerRange(); 128 await animation.finished; 129 assert_equals(animation.playState, "finished", 130 "animation is paused, awaiting trigger event"); 131 assert_times_equal(animation.currentTime, ANIMATION_DURATION_MS, 132 `currentTime is ${ANIMATION_DURATION_MS}`); 133 134 // animation should be reversed by trigger2. 135 rangeBoundaries2.exitExitRangeBelow(); 136 await waitForNextFrame(); 137 assert_equals(animation.playState, "paused", 138 "animation is paused, awaiting trigger event"); 139 assert_times_equal(animation.currentTime, 0, `currentTime is 0`); 140 }, "Triggers on same animation; no conflict."); 141 142 promise_test(async (test) => { 143 await waitForScrollReset(test, scroller); 144 const animation = setupAnimation(); 145 const trigger1 = setupAnimationTrigger(view_timeline1); 146 const trigger2 = setupAnimationTrigger(view_timeline2); 147 148 // Test preconditions. 149 assert_equals(animation.playState, "idle", "animation is idle"); 150 assert_equals(animation.currentTime, null, "currentTime is null"); 151 assert_equals(scroller.scrollTop, 0, "scroller is not scrolled, i.e. " + 152 "not within the trigger range"); 153 assert_array_equals(trigger1.getAnimations(), [], 154 "trigger1 has no animation attached"); 155 assert_array_equals(trigger2.getAnimations(), [], 156 "trigger2 has no animation attached"); 157 158 trigger1.addAnimation(animation, "play-forwards", "reset"); 159 trigger2.addAnimation(animation, "play-forwards", "reset"); 160 161 // Test preconditions. 162 assert_equals(animation.playState, "paused", "animation is paused"); 163 assert_equals(animation.currentTime, 0, "currentTime is null"); 164 assert_array_equals(trigger1.getAnimations(), [animation], 165 "trigger1 has animation attached"); 166 assert_array_equals(trigger2.getAnimations(), [animation], 167 "trigger2 has animation attached"); 168 169 await rangeBoundaries1.enterTriggerRange(); 170 await animation.finished; 171 assert_equals(animation.playState, "finished", 172 "animation is played by trigger1"); 173 assert_times_equal(animation.currentTime, ANIMATION_DURATION_MS, 174 `currentTime is ${ANIMATION_DURATION_MS}`); 175 176 // Entering trigger2's trigger range exits trigger1's exit range. 177 // So trigger1 wants to do a reset and trigger2 wants to play the 178 // animation. As trigger2 was the most recently created, it wins and the 179 // animation is played. 180 rangeBoundaries2.enterTriggerRange(); 181 await animation.finished; 182 assert_equals(animation.playState, "finished", 183 "animation is paused, awaiting trigger event"); 184 assert_times_equal(animation.currentTime, ANIMATION_DURATION_MS, 185 `currentTime is ${ANIMATION_DURATION_MS}`); 186 }, "Triggers on same animation; conflict."); 187 </script> 188 </body> 189 </html>