- From: Brian Birtles <bbirtles@mozilla.com>
- Date: Tue, 14 Apr 2015 16:39:20 +0900
- To: "public-fx@w3.org" <public-fx@w3.org>
Hi, We previously resolved[1] to add some extra methods to the Animation interface. I've been going over this and I'm not sure how much we actually need to change. First, recall the states of an Animation which we can simplify to the following three: idle = no current time (*may* have a start time) paused = has current time but no start time running = has current time and start time 'finished' is (currently) just a variation on 'running', and 'pending' is just an interim state while moving between one of the above three states. We can trigger transitions between these states using three primitive operations: * pause() - clears the start time (and records the current time in the hold time slot). i.e. causes the following transitions idle -> idle [See note (g) below though] paused -> paused running -> pending -> paused (finished -> paused) * play() - resolves a start time. How it does that depends on the current state. If there's a current time we preserve it unless the current time is outside the range of the animation [0, target effect end]. If the current time is outside that range or unresolved we choose a start time that makes the current time equal to the appropriate end of that interval. idle -> running paused -> pending -> running running -> running (finished -> running) * cancel() - clears the start time and current time idle -> idle paused -> idle running -> idle (finished -> idle) (There are exceptions such as when there's no active timeline but this is roughly how it works. There are also finer details such as which endpoints of the animation's range are inclusive, but let's ignore that for now.) The main problems raised with this so far are: a. play() does a conditional seek and it seems like it would be better to have a more primitive operation that *just* resolves the start time when needed (i.e. triggers the 'idle/paused -> running' transition but NOT the 'finished -> running' transition). I thought about this for a while and had some ideas for introducing a more primitive run()/resume()/start() operation. Apart from complicating the API I've come to think that play() isn't that bad. We seek to one of the interval endpoints when transitioning from the 'idle' state so it doesn't seem so bad to do exactly the same thing when transitioning from the 'finished' state. b. Some people want the async methods play()/pause() to return Promise objects. The difficulty of returning a Promise from play() is that people might expect it to return a promise that resolves when the animation reaches the end (the finished promise). Others might it expect it to return the promise that resolves when the play() operation has completed the work necessary to transition the animation to the running state (the ready promise). We could probably resolve this by adding a separate run()/resume() that returns the ready promise and making play() return the finished promise but there was some concern raised about this last time. One of the concerns with returning Promise objects was that for Promise attributes, implementations can lazily create them. For return values, however, implementations have to create them even if they're not used. I think this isn't quite as bad as it first seems because for animations that are played/paused using CSS/SVG/Element.animate(), we wouldn't have to create the Promise object and that probably covers 95% or more of usage? Another concern was that by having play() return the finished promise, we might encourage people to chain animations by Promise chaining. That's not a very good way of chaining animations because you'll get gaps between them and, worse still, those gaps might not be too noticeable on a desktop machine but only show up on mobile. However, making play() *not* return a promise for that reason seems like we'd be penalizing authors with a legitimate use for Promise chaining (e.g. remove the div once the opacity fade has finished) for the sake of protecting other authors from sub-optimal usage which we could point out in other ways? For example, SMIL warns authors that chaining animations with 'begin' and 'end' is preferable to using 'beginEvent' and 'endEvent' for the same reason and I think authors tend to get it right (or at least I haven't seen anyone get it wrong yet). I don't really mind too much, but I'm trying to summarize the argument around this point. c. The ready promise is reused. If you do pause() and then interrupt it with play() we don't create two separate Promise objects, but just reuse the one created when you called pause(). The idea is that the ready promise doesn't resolve until eveything is ready. I'm not sure if this is useful or not. If pause() and play() were to return Promise objects it would probably make more sense that they are separate 'pause-ready' and 'play-ready' promises which get cancelled if the operation is aborted. d. On a related note, should we expose 'play-pending' and 'pause-pending' as separate states? Currently they're both just reported as 'pending'. e. Should 'paused' really win over 'finished'? The fundamental problem here is that 'paused' and 'finished' are really orthogonal. It's possible to be both at once. That said, I'm less than enthusiastic about splitting this out in the API since I think it overcomplicates it (e.g. if you wanted to check if the animation is actually moving you'd have to check anim.playState == 'running' && !anim.isFinished). In regular circumstances you won't end up in the paused-and-finished state since you won't hit the end of the animation until you resume the animation. It's only when you're doing manual seeking or adjusting the duration of in-play animations that this situation arises. If we accept the three fundamental states I proposed at the start of this email and treat 'finished' as a subset of 'running' then I think it makes sense for an animation with no start time and a current time outside the animation's range to be 'paused' (i.e. no change). f. It's odd that if you're paused and call finish(), you don't end up in the 'finished' state. If 'finished' is a subset of 'running', then I think it makes sense for finish() to take you out of the paused state. It could do this by simply resolving the start time appropriately. g. Should calling pause() while idle transition to the 'paused' state? i.e. resolve the current time to the appropriate end of the interval? To create an initially paused animation you currently need to call play() then pause() (or just set the currentTime). Amongst the three primitive operations introduced above there's no operation that triggers an idle->pause transition. Thoughts on any of the above? Best regards, Brian [1] https://lists.w3.org/Archives/Public/public-fx/2015JanMar/0072.html, part2
Received on Tuesday, 14 April 2015 07:39:52 UTC