| <!DOCTYPE html> |
| <html id="top"> |
| <meta charset="utf-8"> |
| <title>View timeline animation events</title> |
| <link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#events"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/web-animations/testcommon.js"></script> |
| <style type="text/css"> |
| @keyframes anim { |
| from { transform: translateX(0); } |
| to { transform: translateX(100px); } |
| } |
| #target { |
| background: green; |
| height: 100px; |
| width: 100px; |
| margin-bottom: 150vh; |
| animation-timeline: view(); |
| } |
| .animate { |
| animation: anim auto; |
| } |
| </style> |
| <body> |
| <div id="target"></div> |
| </body> |
| <script type="text/javascript"> |
| promise_test(async t => { |
| const target = document.getElementById('target'); |
| |
| // Create a timeline and advance to the next frame to ensure that the |
| // timeline has a value for currentTime. |
| await waitForNextFrame(); |
| const timeline = new ViewTimeline({ subject: target }); |
| await waitForNextFrame(); |
| |
| let animationstart_events = 0; |
| let animationend_events = 0; |
| document.addEventListener('animationstart', () => { |
| animationstart_events++; |
| }); |
| document.addEventListener('animationend', () => { |
| animationend_events++; |
| }); |
| |
| // Start the animation and swap out its timeline while still play-pending |
| // so that it already has a value for current time. |
| target.classList.add('animate'); |
| const anim = target.getAnimations(); |
| anim.timeline = timeline; |
| // Introduce a style change that will make the timeline state stale when |
| // "ticked" at the start of the next animation frame. |
| target.style = 'margin-top: 150vh'; |
| |
| assert_false(!!anim.startTime, |
| 'Start time deferred until timeline is updated'); |
| |
| // Verify that we are not evaluating a start time based on a stale timeline. |
| await waitForNextFrame(); |
| await waitForNextFrame(); |
| assert_equals(animationstart_events, 0, |
| 'Target initially off-screen and no animationstart event'); |
| assert_equals(animationend_events, 0, |
| 'Target initially off-screen and no animationend event'); |
| |
| const scroller = document.scrollingElement; |
| scroller.scrollTop = target.getBoundingClientRect().top; |
| await waitForNextFrame(); |
| await waitForNextFrame(); |
| |
| assert_equals(animationstart_events, 1, |
| 'scrollstart event received after scrolling into view.'); |
| assert_equals(animationend_events, 0, |
| "No scrollend event until after scrolling out of view"); |
| |
| scroller.scrollTop = target.getBoundingClientRect().bottom; |
| |
| await waitForNextFrame(); |
| await waitForNextFrame(); |
| |
| assert_equals(animationstart_events, 1, |
| 'No additional scrollstart event'); |
| assert_equals(animationend_events, 1, |
| 'scrollend event received after scrolling out of view'); |
| }, 'View timelime generates animationstart and animationend events'); |
| </script> |