Synchronising script with animation events
Brian Birtles <[email protected]> Fri, 16 Mar 2012 15:01:12 +0900
| Newsgroups | gmane.comp.mozilla.devel.dom |
|---|---|
| Message-ID | <[email protected]> |
Dear all, Together with a few others, I'm currently working on specifying a means for synchronising script and animation. The problem is that it is not possible to create animations in response to CSS animation events that remain in sync with other running animations. Consider this use case: * Animation A has a duration of 2s. * Animation B has a duration of 4s. * When animation A finishes, an 'animationend' event is dispatched and the handler conditionally chooses animation C to run next. Animation C has a duration of 2s. The desired result is that animation C finishes at the same time as animation B. This is currently not the case. Test case here: http://people.mozilla.org/~bbirtles/css-sync.html In Gecko the visual result looks pretty good on fast machines with simple animations (however, the debug output indicates the event handler is not being run precisely at the end of the first animation). In WebKit the lag is often evident even in simple animations. Adobe have some more complex examples where the lack of synchronisation is really obvious. Zooming in on the script interaction at t=2s, the sequence of events is something like the following: 1. Animation on 'a' finishes 2. Next refresh driver sample occurs 3. 'animationend' event is queued 4. 'animationend' event handler is run 5. New animation on 'c' begins Ideally we want all these steps to happen in the same instant. In Gecko I *think* we can have gaps--i.e. periods where the "animation time" continues to advance--between 1 and 2, and between 3 and 4. In WebKit I *think* the gaps are between 3 and 4, and between 4 and 5. The approach that Flash takes is to pause the animation until the event handlers have run. (Presumably, it is paused at the time of the event.) After the event handlers finish, the animation does a catch-up to the actual time it would be at if it wasn't paused. This provides the following two advantages: A) Event handlers can make assumptions about "what is on the stage"--i.e. the state of the animation. B) Animations created/modified by event handlers will be in sync with other animations. Obviously if event handlers do expensive operations you'll see stutter so authors are warned not to do that. I've been talking with a few others about how to provide this kind of synchronisation for scripted interaction with animations on the Web platform and the possibilities we've generated so far are: i) Mimic Flash's pausing behaviour. The main concern here is how well this approach scales to a Web context where there could be many events in the queue. Also, with a wider range of implementations available than Flash simply saying "don't do expensive things in your event handler" doesn't really cut it since what's expensive can vary and an animation that doesn't stutter on one implementation may look terrible on another. One suggestion to ameliorate this is to introduce animation groups that limit the scope of the pausing. At an implementation level, on each sample you'd check if there were animations endpoints (with registered event handlers) between the last sample and the "current time". Then you'd advance the animation time to the earliest of those times and block all updates to the animation time until the event handlers for that time have run. When the last of those event handlers runs, you'd repeat the process until the animation time matches the current time. Depending on how event processing and the refresh driver interact, and what mechanism is used to detect when all event handlers have run, you could run into one of two problems: * If the event handlers for all animation endpoints between the previous sample and the current time are run before the next sample arrives, you could potentially end up doing expensive operations like reflow much more frequently than the refresh rate. But perhaps that is already the case? * If you don't run all event handlers up to the current time (and consequently don't update the animation time to the current time) then you could have animations that lag badly or fall further and further behind. ii) As with (i) but prioritise these animation events somehow (has there been any talk of doing that before?) to try and minimise stutter. I'm not sure how much this would actually help though. iii) Don't "pause" the animation, just run the event handlers synchronously like we do for MutationEvents (which, I know, everyone hates). That is, eliminate the gap between 3 and 4 altogether. However, you'd still want to step through the animation endpoints to remove the potential time gap between 1 and 2 which means you'll run into the problem mentioned above of potentially doing reflow etc. more frequently than the refresh rate. iv) Introduce the notion of a separate play and edit cursor. The play cursor's time corresponds to "current time" and the edit cursor's time corresponds to when the event was dispatched. Presentation uses the animation time of the play cursor; when event handlers query values and create animations, the animation time of the edit cursor is used. A naive implementation would simply do a backwards seek from the play cursor time to the edit cursor time every time an event handler was run. Either the timing model needs to be defined such that a backwards seek was cheap or the implementation needs another means of tracking the different states. Note that with this approach you could potentially see animations you're not supposed to. For example, if an event handler cancels an animation then you might see the animation running for a moment before the event handler gets a chance to run. v) Provide a means for getting the event time and producing animations based on that. For example, you could get the time of the event from the event itself, and then use that to generate a new animation that starts at that time. The problems with this are: * It doesn't solve (A)--i.e. you can't make assumptions about what is on the stage * You have the problem of seeing animation you shouldn't * It requires adding the concept of absolute times to CSS (which we probably need anyway) I'd really appreciate any input on this. My main concern is to solve this at the spec level so authors have guaranteed synchronisation behaviour. Are there any specs that already address this? Alternative proposals? Implementation considerations? Thanks, Brian Birtles