support.js (4224B)
1 // Assert that the user agent under test supports AnimationTrigger. 2 // AnimationTrigger tests should do this sanity check before proceeding. 3 function assertAnimationTriggerSupport() { 4 assert_true(document.documentElement.style.animationTrigger !== undefined); 5 } 6 7 const setScrollTop = (scroller, y) => { 8 const scrollend_promise = 9 waitForScrollEndFallbackToDelayWithoutScrollEvent(scroller); 10 scroller.scrollTop = y; 11 return scrollend_promise; 12 } 13 14 function getRangeBoundariesForTest(trigger_start, trigger_end, 15 exit_start, exit_end, scroller) { 16 let rangeBoundaries = { 17 scroller: scroller, 18 offsetWithinTriggerRange: (trigger_start + trigger_end) / 2, 19 offsetAboveTriggerRange: trigger_start - 10, 20 offsetBelowTriggerRange: trigger_end + 10, 21 offsetAboveExitRange: exit_start - 10, 22 offsetBelowExitRange: exit_end + 10, 23 }; 24 25 rangeBoundaries.enterTriggerRange = async () => { 26 return setScrollTop(rangeBoundaries.scroller, 27 rangeBoundaries.offsetWithinTriggerRange); 28 }; 29 rangeBoundaries.exitTriggerRangeAbove = async () => { 30 return setScrollTop(rangeBoundaries.scroller, 31 rangeBoundaries.offsetAboveTriggerRange); 32 }; 33 rangeBoundaries.exitTriggerRangeBelow = async () => { 34 return setScrollTop(rangeBoundaries.scroller, 35 rangeBoundaries.offsetBelowTriggerRange); 36 }; 37 rangeBoundaries.exitExitRangeAbove = async () => { 38 return setScrollTop(rangeBoundaries.scroller, 39 rangeBoundaries.offsetAboveExitRange); 40 }; 41 rangeBoundaries.exitExitRangeBelow = async () => { 42 return setScrollTop(rangeBoundaries.scroller, 43 rangeBoundaries.offsetBelowExitRange); 44 }; 45 46 return rangeBoundaries; 47 } 48 49 // Helper function for animation-trigger tests. Aims to perform a scroll and 50 // observe the animation events indicated by |events_of_interest| and 51 // |events_should_fire| 52 async function testAnimationTrigger(test, scroll_fn, target, 53 events_of_interest, events_should_fire) { 54 assertAnimationTriggerSupport(); 55 56 let evt_promises = []; 57 for (let idx = 0; idx < events_of_interest.length; idx++) { 58 const evt = events_of_interest[idx]; 59 const animationevent_promise = new Promise((resolve) => { 60 const watcher_func = () => { 61 if (!events_should_fire[idx]) { 62 test.unreached_func(`received unexpected event: ${evt}.`)(); 63 } 64 resolve(); 65 } 66 67 target.addEventListener(evt, watcher_func, 68 { once: true }); 69 70 // If we are not expecting the event, just wait for 3 frames before 71 // continuing the test. 72 if (!events_should_fire[idx]) { 73 waitForAnimationFrames(3).then(() => { 74 target.removeEventListener(evt, watcher_func); 75 resolve(); 76 }); 77 } 78 }); 79 80 evt_promises.push(animationevent_promise); 81 } 82 83 await scroll_fn(); 84 await Promise.all(evt_promises); 85 } 86 87 function computeContainOffset(scroller, subject, pct) { 88 const contain_start = subject.offsetTop + subject.offsetHeight 89 - scroller.offsetTop - scroller.clientHeight; 90 const contain_end = subject.offsetTop - scroller.offsetTop; 91 92 return contain_start + (pct / 100) * (contain_end - contain_start); 93 } 94 95 function setupAnimationAndTrigger(target, subject, duration) { 96 const animation = new Animation( 97 new KeyframeEffect( 98 target, 99 [ 100 { transform: "scale(1)", backgroundColor: "yellow" }, 101 { transform: "scale(2)", backgroundColor: "yellow" }, 102 ], 103 { duration: duration, fill: "both" } 104 )); 105 106 let trigger = new TimelineTrigger({ 107 timeline: new ViewTimeline({ subject: subject, axis: "y" }), 108 rangeStart: "contain 0%", 109 rangeEnd: "contain 100%" 110 }); 111 112 trigger.addAnimation(animation, "play-forwards", "play-backwards"); 113 } 114 115 async function waitForAnimation(targetCurrentTime, animation) { 116 return new Promise(resolve => { 117 function waitForCurrentTime() { 118 if ((targetCurrentTime - animation.currentTime) * animation.playbackRate <= 0) { 119 resolve(); 120 return; 121 } 122 123 requestAnimationFrame(waitForCurrentTime); 124 } 125 waitForCurrentTime(); 126 }); 127 }