Shane would like to try and implement the approach where we have animations that don't add to other instances of the same template and see how it feels. We’ll reconsider based on that feedback if this is a suitable approach.
Discussion on reversing: currently reversing requires breaking the link with the template because you need to change the playback rate and fill mode. This shouldn't be—i.e. it should be possible to reverse something without breaking the link to the template. Either add override playback rate and override fill mode to the Anim instance or some special reversing flag.
4. SEQUENCE CONTAINERS AND MEDIA CONTROLLERS
Pausing inside a sequence container is a bit weird. Currently I've defined endTime so that, for example, if the item is paused, the endTime is infinite. This gives you the behaviour where if you pause one child the next one won't start until it is unpaused (since the next child's start time is based on the previous child's endTime).
I'm pretty sure that's what you want since once you add media elements to the equation if you pause or seek a video, you expect subsequent children in the group to react to its new end time.
(For example, pause the video for 2 seconds and the endTime gets 2 seconds later. Skip forward 3 seconds in the video and the endTime becomes 3 seconds earlier.)
But it's weird for a few reasons:
1) If you play a sequence container that contains a paused descendent in reverse all you'll see is that first frame of the paused animation. You won't get the behaviour where it plays the children in reverse sequence and then pauses at the last frame of the paused child.
2) Suppose, while child B is playing, you pause it for 2 seconds then resume. If you then seek back to the start of the container you'll get A playing, then 2 seconds of nothing, then B playing. (Effectively, by pausing the child, you've shifted its time by 2 seconds)
3) It's a little odd that endTime reacts to pausing/seeking but startTime does not. Also endTime can come before startTime (due to negative delays or seeking forwards).
I think there are a few possibilities here:
A) Add a facility to reset the timeDrift
Basically TimedItem.reset(). It does a seek to time 0 and clears the timeDrift and locallyPaused state of all descendants.
This fixes (2) and is something you often want to do.
B) Auto-reset time drift
Basically A, but if you do a seek to a time before the beginning of the animation interval you clear the timeDrift automatically.
This kind of matches my intuition of what should happen but it breaks the idea that the model is stateless. That's a problem because implementations have to be very careful to detect all circumstances when the effective time is before the start of an animation interval with non-zero timeDrift. It sounds like a real source of interop problems, or worse, problems that only crop up in some implementations when the frame rate is low (if they just check at sample time).
So I think the explicit call is better.
C) Slave sequence groups' pause state and seek time to their children (or actually descendents)
There seem to be a lot of parallels between the way sequence groups and media controllers work. You could just defer all pause requests to the container. That doesn't let you run child A while child B is paused however. Pausing child B would pause the group meaning child A won't run.
Seeking however, could work fairly intuitively. You'd convert the times to group time and then apply the seek to the group. The counter-intuitive bit is when A is playing and you seek B... you'd skip A and start B. You also lose flexibility... you can't shift just one child back / forward.
For this to work however, i.e. to solve (2), I think this has to apply to all descendents. In effect, any animation with an ascendent sequence group is slaved.
D) No seeking a descendent of a sequence group, pausing is slaved to the group
That's how HTML5 media controllers work. You can't set currentTime if you have a media controller. Pausing is slaved. With play(), if you have a media controller you skip the seeking step.
For issue (1) above (the fact that you can't really play a sequence group in reverse if it has a paused child) I think you just have to say, "that's how the model works". To do otherwise would break down the ability to randomly sample the model.
For issue (3) above (the fact that endTime reacts to pauses/seeks but startTime doesn't, and also the fact that endTime can come before startTime), we could possible rename endTime to animEnd.
Discussed the issue but also with reference to par groups that repeat:
• One proposal was to have a type of par group that cannot be iterated, a “loose” group. For all other types of groups, pausing a descendant pauses the group (i.e. pausing anywhere pauses everywhere).
- questions about whether exposing this kind of group is actually useful or just confusing. An alternative would be to make the document timeline such a “loose” group (in effect) but not allow creating such groups elsewhere (i.e. just make it special behaviour defined on the timeline).
- concern that if a group gets added at any point (e.g. a <svg> is mapped to a par group) then suddenly you can’t independently pause children — this may be reason to have loose groups other than just the document timeline
- question: why would you do that? groups are for synchronising. Don't use them unless you want synchronisation.
• Another proposal was to ignore child pause state when scheduling par and seq groups. This results in some “weird” behaviour — e.g. sequences getting out of order because pausing one item doesn’t result in the next item failing to play. Of course you can always pause the sequence…
- also concern about whether this is fundamentally different to the cascading nature of time in the model defined so far (but conversely, why should pausing a child modify an explicitly set iterationDuration on a parent?)
> This requires more thought but the "loose" group idea seems to hold some promise. Not sure yet if they should be generally available or just specified on the top-level (timeline) group.
5. PAUSING OUTSIDE THE ANIMATION INTERVAL
A while ago we had a discussion about what happens if you pause a child outside its animation interval. The expected behaviour in that case is that it has no effect until the animation starts.
For example, I have
At t=3s, I pause anim.
The expected behaviour we thought was that time continues to tick and then at t=10s, the animation starts paused (i.e. the first frame appears and doesn't move).
Presumably the same would happen in reverse.
Currently we don't do that. Pausing at t=3s, pauses at t=3s. If you unpause it, you still have to wait 7s before the things starts.
Supporting the behaviour where times outside the animation interval don't contribute to the pause is possible. I even wrote out the algorithm:
Inside the calculation for timeDrift:
If locallyPaused is false
... as before ...
If locallyPaused is true
Let anim start = startTime + timing.startDelay
If pause start < anim start
If effective parent time < anim start
return max(0, effective parent time - anim start)
Let anim end = anim start + animDuration + stored value of timeDrift
If pause start >= anim end
If effective parent time > anim end
return stored value of timeDrift
return max(0, anim end - effective parent time)
return effective parent time - startTime - pause start
Anyway, that's just there for posterity, but it introduces complexity because if you're paused at t=3s in the previous example, and do a seek to t=0s what do you expect?
If we want to make that take effect I think internally we'll need to separate out pauseDrift and seekDrift (which basically equates to the "stored value of timeDrift" above).
I just want to check if this is really the behaviour we want.
> After some discussion, we agree that if you pause during the delay phase it should take effect. In essence, pause takes effect at the earlier of two moments, the start time or the animation interval start. This is needed both for CSS compatibility and because in our model the delay is part of the animation.
6. ISSUES FOR NEXT TIME
* How to seek when currentTime is null
-- Brian and Shane discussed this --- basically divorce currentTime from item time. We said currentTime always exists and can be set, but is sometimes not live; if it's live then currentTime == itemTime. In a sense, currentTime is the item time calculated using the "effective parent time" as defined in the spec.
* Specified start times should not update when moving animations between containers, but automatically calculated start times should. (However, if the start time was specified with the intention of being a relative amount from the parent's iteration time then it *should* update). We (Shane and Brian) also discussed adding startTime as a parameter to add, and always updating startTime.
* Sensible behaviour for changePlaybackRate(0)
-- If you call changePlaybackRate(0) mid-way through an animation you expect it to pause. Currently it does not because a playback rate of zero means an infinite duration which means you never get past the starting point. You can work around this by adjusting the iterationStart, or by just special-casing 0 to make it actually pause, or you can just say "that's the model". What do you prefer?
* Integrating with video and audio... is this really going to work?
-- video and audio need a concept of direction but our timing functions etc. mean all bets are off regarding the direction of the next sample. And its not just enough to disallow certain timing features on the video since these things can happen all the way up the tree. Maybe we need some process where we approximate the effect... e.g. walk up the tree, ignore timing functions, work out the direction, playbackRate etc. and then apply those properties to the video itself (behind the scenes?)