| <!DOCTYPE html> |
| <html> |
| <head> |
| <link rel="help" src="https://drafts.csswg.org/css-animations-2/#animation-trigger"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/web-animations/testcommon.js"></script> |
| <script src="/dom/events/scrolling/scroll_support.js"></script> |
| <script src="support/support.js"></script> |
| </head> |
| <body> |
| <style> |
| @keyframes myAnim { |
| from { transform: scaleX(1); } |
| to { transform: scaleX(5); } |
| } |
| .subject, .target { |
| height: 50px; |
| width: 50px; |
| background-color: red; |
| } |
| .target { |
| animation: myAnim linear 0.5s both; |
| animation-trigger-type: repeat; |
| animation-trigger-range: 250px 300px; |
| animation-trigger-exit-range: 200px 350px; |
| } |
| .scroll { |
| animation-trigger-timeline: scroll(); |
| } |
| .view { |
| animation-trigger-timeline: view(); |
| } |
| .deferred { |
| animation-trigger-timeline: --viewtimeline; |
| } |
| .deferred.subject { |
| view-timeline: --viewtimeline; |
| } |
| .scroller { |
| overflow-y: scroll; |
| height: 500px; |
| width: 500px; |
| border: solid 1px; |
| position: relative; |
| } |
| #wrapper { |
| timeline-scope: --viewtimeline; |
| } |
| #space { |
| width: 50px; |
| height: 600px; |
| } |
| </style> |
| <div id="wrapper"> |
| <div id="scroll_scroller" class="scroller"> |
| <div id="space"></div> |
| <div id="scroll_target" class="scroll subject target" tabindex="0"></div> |
| <div id="space"></div> |
| </div> |
| <div id="view_scroller" class="scroller"> |
| <div id="space"></div> |
| <div id="view_target" class="view subject target" tabindex="0"></div> |
| <div id="space"></div> |
| </div> |
| <div id="deferred_scroller" class="scroller"> |
| <div id="space"></div> |
| <div id="deferred_subject" class="deferred subject" tabindex="0"></div> |
| <div id="space"></div> |
| </div> |
| <div id="deferred_target" class="deferred target" tabindex="0"></div> |
| </div> |
| <script> |
| // // Each test case sets these. |
| // let scroller; |
| // let target; |
| |
| async function testRepeatAnimationTrigger(test, rangeBoundaries) { |
| const initial_transform = getComputedStyle(target).transform; |
| // Just short of the trigger range start, no trigger action expected. |
| await testAnimationTrigger(test, () => { |
| return rangeBoundaries.exitTriggerRangeAbove(); |
| }, target, ["animationstart", "animationend"], [false, false]); |
| |
| // This skips the trigger range and should not play the animation. |
| await testAnimationTrigger(test, () => { |
| return rangeBoundaries.exitTriggerRangeBelow(); |
| }, target, ["animationstart", "animationend"], [false, false]); |
| |
| // This enters the trigger range and should play the animation. |
| await testAnimationTrigger(test, () => { |
| return rangeBoundaries.enterTriggerRange(); |
| }, target, ["animationstart", "animationend"], [true, true]); |
| |
| // This is a repeat trigger, exiting the exit range resets the |
| // animation. By inspecting style.transform, we should see that the |
| // animation was reset. |
| await testAnimationTrigger(test, () => { |
| return rangeBoundaries.exitExitRangeAbove(); |
| }, target, ["animationstart", "animationend", "animationcancel"], |
| [false, false, false]); |
| assert_equals(getComputedStyle(target).transform, initial_transform); |
| |
| // This is a repeat trigger, re-entering plays the animation. |
| await testAnimationTrigger(test, async () => { |
| // Enter the range. |
| await rangeBoundaries.enterTriggerRange(); |
| await waitForAnimationFrames(5); |
| // Exit the range. |
| return rangeBoundaries.exitExitRangeBelow(); |
| }, target, ["animationstart", "animationend", "animationcancel"], |
| [true, false, false]); |
| } |
| |
| const CSS_TRIGGER_START_PX = 250; |
| const CSS_TRIGGER_END_PX = 300; |
| const CSS_EXIT_START_PX = 200; |
| const CSS_EXIT_END_PX = 350; |
| |
| promise_test(async (test) => { |
| scroller = scroll_scroller; |
| target = scroll_target; |
| |
| const rangeBoundaries = getRangeBoundariesForTest(CSS_TRIGGER_START_PX, |
| CSS_TRIGGER_END_PX, |
| CSS_EXIT_START_PX, |
| CSS_EXIT_END_PX, |
| scroller); |
| await testRepeatAnimationTrigger(test, rangeBoundaries); |
| }, "repeat animation triggered via scroll() timeline."); |
| |
| promise_test(async (test) => { |
| scroller = view_scroller; |
| target = view_target; |
| |
| const COVER_START_OFFSET = 100; |
| |
| const rangeBoundaries = getRangeBoundariesForTest( |
| COVER_START_OFFSET + CSS_TRIGGER_START_PX, |
| COVER_START_OFFSET + CSS_TRIGGER_END_PX, |
| COVER_START_OFFSET + CSS_EXIT_START_PX, |
| COVER_START_OFFSET + CSS_EXIT_END_PX, |
| scroller); |
| await testRepeatAnimationTrigger(test, rangeBoundaries); |
| }, "repeat animation triggered via view() timeline."); |
| |
| promise_test(async (test) => { |
| scroller = deferred_scroller; |
| target = deferred_target; |
| |
| const COVER_START_OFFSET = 100; |
| |
| const rangeBoundaries = getRangeBoundariesForTest( |
| COVER_START_OFFSET + CSS_TRIGGER_START_PX, |
| COVER_START_OFFSET + CSS_TRIGGER_END_PX, |
| COVER_START_OFFSET + CSS_EXIT_START_PX, |
| COVER_START_OFFSET + CSS_EXIT_END_PX, |
| scroller); |
| await testRepeatAnimationTrigger(test, rangeBoundaries); |
| }, "repeat animation triggered via deferred (view) timeline."); |
| </script> |
| </body> |
| </html> |