(Follow up after having read the appropriate docs)
3. INTEGRATION WITH MEDIA
I think we need to work on this ASAP since it will influence the architecture.
Thinking out aloud:
Can we replace groups with media controllers and just add a 'seq' mode to media controllers? Which effectively calls play() at the appropriate times?
I think a more feasible route is to have groups subclass media controllers.
Then you can put stuff in a group either by nesting or by setting the mediaGroup attribute.
You could restrict groups to only contain groups or animations (and not other media controllers) but maybe even that restriction is not necessary
In that case it might be better to rename animation groups to TimingGroup (MediaGroup? TimingController?)
The ideal outcome here would also allow us to address the outstanding issue of how calling pause() or reverse() etc. should work for animations that are in seq containers or repeating par containers. Options include, making pause():
pause the group
throw an exception
handle timeDrift specially such that the pause component resets when currentIteration changes
Pausing the group seems attractive. It's NOT how MediaControllers work but it's not entirely different either. For example, calling play() on a MediaController calls play() on all the slaved elements. So we could say these groups are a special type of MediaController.
Adopting the architecture outlined in item 6 of the agenda means most animations would not be subject to these constraints. It would only be when you specifically created a group that you'd encounter this.
(Déjà vu: under this arrangment, and presuming animations can be associated with vanilla HTML5 MediaControllers, it feels a little bit like these regular MediaControllers would end up become equivalent to the "loose" par groups we floated back in March/April but with the critical difference that they can't be repeated so we don't have to worry about how pausing works in that case.)
In terms of aligning better with HTMLMediaElement some possible adjustments:
Adjust autoplay behaviour to match
I haven't looked into the specifics of this but I think this would be highly desirable
Add a read-only boolean ended attribute (marks when animation has finished). Note this is stative but we seem to be moving towards a model where the methods on TimedItem itself are largely stative whilst the Timing structure is stateless. Its definition would basically be in terms of whether the previous sample was greater than or equal to (endpoint-exclusive timing) the endTime.
In HTMLMediaElement, changes to playbackRate preserve the play position in the same way our changePlaybackRate does, i.e. it's stative.
I wonder if we can have some arrangement whereby we can match that behaviour and also provide a stateless version too.
The most obvious is to have Anim.playbackRate adjust the rate whilst preserving position and Anim.timing.playbackRate simply adjust the model in a stateless way (causing a jump). Obviously that's horrible as describe in the next section where we'll consider alternatives.
Note that HTMLMediaElement does have defaultPlaybackRate which is a little bit like a stateless version. It's different--basically it takes effect, for example, when the user stops fast-forwarding--but it feels similar in some sense.
Also, in HTMLMediaElement, playbackRate can be set to zero and it will preserve the current position.
We can't easily do this in our current model. I've yet to come up a good way of achieving it without either introducing magic or touching other values like iterationStart.
4. SMALL FRY
a. We floated the idea of implementation on webkit-dev and received some push-back. Notably, some thought that name shortening was "unwebby". Should we revert Anim → Animation, Func → Function? Or do we have "webby" examples of name shortening we can point to?
Brian: I sympathise with this criticism.
Anim → Animation
To address the concern about this being too long to type I wonder if we can extend Element with an animate method? (a la Raphaël)
AnimGroup → AnimationGroup
This happens to match the naming in Core Animation
ParAnimGroup → ParGroup?
SeqAnimGroup → SeqGroup?
(These 'par' and 'seq' abbreviations already have established usage due to SMIL and I expect an element syntax would probably use these names. ParallelGroup is particularly error-prone to type especially for non-native English speakers.)
TimingFunc → TimingFunction (you don't type this as often except when you access anim.timingFunction)
AnimFunc → AnimationFunction (see below)
2012-11-29[Brian]: Made the above changes but I still want to get feedback about extending Element.
Shane: One major piece of feedback in general is that the spec looks huge. It is, but the implementation size is small. The polyfill will help here, and a primer might too. One of our engineers is interested in maybe writing a primer. Good idea?
b. 6.13.2. point 3 really needs some explanatory text.
c. does iterationStart = 0.5 & iterationCount = 2 cause (iteration + timeFraction) to start at 0.5 and sweep to 2.5? If so, why is iterationStart capped to iterationCount?
d. what should happen if timing parameters are changed after an animation is finished?
var a = Anim(..., 2)
// @ 3 seconds
a.timing.duration = 4
var a = Anim(..., 2)
// @ 5 seconds
a.timing.duration = 4
var a = Anim(..., 2)
// @ 3 seconds
a.timing.iterationCount = 2
e. How about a root timeline with global time that underlies the document timelines? This would enable global-clock-synchronized animations (e.g. clock-tick animations and the like) at very little additional complexity cost.
f. Why do we have startDelay but not between-iteration-delay or endDelay? Triggers partly address this.
g. Should we expose reversing state? Should it be reversed / locallyReversed?
5. ANIMATION STACK AND KEYFRAMES
(Continuing actions from last time regarding merging and behaviour where no keyframe is defined)
6. THE CHOPPING BLOCK
What can we drop from v1 to make things simpler and faster to ship?
My (Brian's) suggestions:
Lots of complexity here. I think if we make animations easily cloneable and add convenience methods that take a DOMNodeList/sequence and generate the necessary animations we'd get 80% of the convenience. For controlling multiple running animations we have groups that cover some of the cases.
The main argument for keeping templates that I can see is because implementations are going to have to implement something like templates anyway in order to manage changes to animations generated by markup so why not formalise this behaviour from the start?
It's great but I'm not sure if it's necessary for currently pressing use cases. We should still work on it to make sure when we do go to add it the architecture doesn't explode.
Sharing of Timing objects
Really, how often will this be done?
This adds a lot of complexity (just see the massive TODO there) and if we have SmoothTimingFunc then I think we can cover most cases.
2012-11-29[Brian]: I already removed this feature
Easy to calculate, but necessary?
Again, simple, but necessary? Would pause(), play() and unpause() be enough? (We currently don't have unpause but MediaControllers do. The difference is basically that it wouldn't auto-rewind.)
Suggest we make another doc for dumping v2 spec text into. When dropping features we can put them there so we have something to start from.
Authors are going to trip up on this and curse, "Who wrote this crazy spec!?!"
It would be ok if we had this pattern throughout the API and then authors could learn it once and deal with it. But we don't.
Also, we have the following situation:
As described in the previous point, in order to align with HTMLMediaElement, we possibly want a playbackRate that is stative and one that's not? And possibly a defaultPlaybackRate?
For reversing we currently change the playbackRate and fillMode. This is not great since it means (with current template behaviour) you can't reverse an animation without breaking the link to the template.
Templates aside, it seems like you should be able to reverse an animation without changing its serialised state. i.e. it should be a runtime control. This might be solved by adding a locallyReversed member as proposed above, but it's somewhat confusing to have three orthogonal attributes controlling direction (playbackRate, direction, locallyReversed).
A) Rename Anim.duration to Anim.calculatedDuration
Problem: breaks consistency with HTMLMediaElement
B) Follow HTML5's naming scheme here, i.e. collapse the Timing member into TimedItem itself so you'd have
Here the 'default' versions are the inputs
The non-default versions are calculated values
Setting the non-default versions overrides the default ones
Setting the non-default version to null reverts to the calculated value
When serialising, you'd typically serialise just the default values. The others should be considered run-time controls
Make 'duration' writeable
Problem: interface bloat (heaps of members in TimedItem)
Problem: not quite the same as HTMLMediaElement. If you set defaultPlaybackRate in HTMLMediateElement it has no immediate effect but here it would (unless playbackRate was overriden)
Problem: Using 'null' to reset stuff is a little hacky? There's also a little bit of magic involved since you can't tell just from inspecting the object whether the non-default values are overridden or are calculated from the default values (unless of course the values differ)
C) Similar to above, but basically rename Anim.timing to Anim.default giving
Basically copy those members from the Timing interface that could be useful to override on a run-time basis to the TimedItem interface
As above, make Anim.duration writeable
Problem: Same problems with using 'null' to reset stuff
'initial' might be better than 'default'?
D) Drop the defaults altogether and put everything directly onto the TimedItem interface. e.g.
Initially the values all reflect the computed values but can be overridden
Setting a value to null reverts to the computed value
i.e. the initial inputs are magically stored away somewhere. It's basically the same as the previous suggestion but with 'default' hidden
Problem: lots of magic, not really possible to serialize the animation apart from its runtime state
E) Just make Anim.duration writeable
Then authors don't really have to worry whether they set Anim.duration or Anim.timing.duration as long as they're consistent
Problem: doesn't help with the other cases
Problem: not great. If a library starts setting Anim.duration while the author is in the habit of setting Anim.timing.duration they might be surprised that all their changes get clobbered.
F) Add accessors for stuff
Problem: slower, more string manipulation
Brian: Lean towards D or F or maybe C.
It's worth considering this in light of templates (assuming we keep them--see the chopping block item at the end).
We already decided to revisit how templates work regarding liveness.
We agreed it's preferable, if possible, to make properties individually overridable.
One difficulty however is how represent the 'inherited' and 'overriden' values in the API
We could, potentially, treat them in a similar way.
e.g. (1), if we go with C above
Anim.duration is calculated from Anim.default.duration is calculated from Anim.template.timing.duration.
* Includes a PauseAnimation -- temporal equivalent of a spacer gif
-- worth adding?
-- Shane: I have an alternative approach that is simpler than a new primitive (and also useful for
the trigger stuff :))
-- AnimFunc = Action --- I think that's clearer
Actually I think we want to be able to have Actions that are just once-off changes. Basically like SVG's <set> animation. They could be modelled as zero duration with forwards fill or something similar. They would often be used from a script to, for example, trigger an XHR request, update some state, change the window title, etc. The callback would be simpler and would typically only be called once (more specifically, it would only be called when the previous sample was on the other side of the startTime + startDelay)
All that is to say maybe we could have Actions and TimedActions
Shane: I'm not opposed to renaming AnimFunc Action. Not sure about the distinction between TimedActions and Actions.
(Actually, on further inspection I'm not sure I quite understand the distinction between Actions and Animations in the QML model)
-- They don't have groups. Should we just call ParAnimGroup, ParAnim?
-- Shane: I think distinguishing between groups and root animations is useful.
* Uses ms (integer) for duration. window.setTimeout etc. use ms as well. Perhaps ms are more common on the web platform? Should we use ms instead? And if so, is an integer too imprecise?
Shane: No! Seconds are really easy to use and make code look good.
Brian: some data gathering...
window.setTimeout - long - milliseconds
window.setInterval - long - milliseconds
XMLHttpRequest.timeout - unsigned long - milliseconds
At a glance it looks like we're pretty close since it's based on SMIL and CSS Transitions/Animations is based on Core Animation.
Some naming suggestions:
Core Animation uses beginTime and timeOffset -- it has the same double-offset!!!! We are not crazy!
Some renaming possibilities here:
startTime: 'start' just seems friendlier, matches HTML5 text tracks
beginTime: matches CoreAnimation, close to SVG (begin attr)
NOTE: HTMLMediaElement has startDate (was startTimeOffset/startTime/initialTime) which is the initial offset into the resource
startDelay: close to CSS (animation-delay)
beginDelay: relationship to beginTime is obvious (assuming we go with that), close to CSS (animation-delay)
beginOffset: relationship to beginTime is obvious (assuming we go with that), close to Core Animation (timeOffset)
NOTE: HTMLMediaElement has startDate (was startTimeOffset/startTime/initialTime) which is the offset into the resource
Core Animation uses fillMode where we have fill
fillMode: matches enum name (FillMode), Core Animation (fillMode), CSS (animation-fill-mode)
fill: matches SVG, shorter
Core Animation uses timingFunction where we have timingFunc (see above where I suggest we go with timingFunction)
Core Animation uses speed where we have playbackRate
the reason we went with playbackRate was to match HTMLMediaElement. I think that wins.
Our Timing object is represented in CoreAnimation by a MediaTiming interface
Core Animation uses parent time, active local time (=our item time), basic local time (=our iteration time)
There's no animation time but even in our model, animation time is really an implementation detail. In terms of the API it's irrelevant. Maybe we should try to hide it a bit more. The time spaces diagram appears quite complicated.
Should we rename?
iteration time -> basic time (SMIL calls this simple time)
animation time -> active time (SMIL calls this active time)
11. FEEDBACK FROM ROB ARNOLD
"I think it would be helpful to have examples to use examples for individual features (ex: seeking; I haven't seen a need for that yet)"
" and indicate what areas the API is intended to be used for (games, rich content, ui?)"
"I am sadly quite well aware that CSSOM for Transitions/Animations is painful so having a good API there which addresses mutation (O(n^2) to remove all keyframes of an animation in WebKit) and triggering (no awkward forced synchronous style calc) would be helpful. "
"One thing that I do think is important but not addressed in the spec is concurrency (I assume that this is what you meant by hardware accelerated animations). In particular, asynchronous animations are highly useful in covering up main thread lag due to events or implementations beyond the author's control. This is key on mobile where performance expectations are high due to a direct manipulation UI but processing power is weak. As a motivating example, consider trying to implement a feed of mixed content (say, Facebook's newsfeed) with native quality scrolling. Things like garbage collection, image decoding, style/layout from new content being inserted and time mysteriously unaccounted for (thanks Chrome!) disrupt the framerate during/transitioning into fling scrolling (which can use CSS animations for better physics / dependable scroll position events). The current and proposed APIs seem to assume some sort of synchronous execution between the JS and on screen animation; for this reason I like asynchronous events so that the animation thread isn't stalled by content."
I (Brian) followed this up and Rob provided the following clarification: "I think there are some implicit assumptions in the API such as the relation between the start of an animation and when you tell it to start; with asynchronous animations, you'll need to account for a delay (due to vsync, IPC overhead, etc...) and the page may want to take that into account when computing its UI."
"I do like that you can compute the animation in JS with your own internal structure and then have the browser query that instead of shoving it into the CSSOM (creating a lot of garbage via string allocation)."
I asked, "I just want to check I've understood this part. Are you referring to CustomAnimFunc? If that's the case, then the user-supplied function just applies the effect itself. It doesn't return anything to the browser."
To which Rob responded: "Oh, I thought that it was to allow JS to specify the animation curve so that the browser can asynchronously render it. This is an example of where I mean that the spec does not seem to permit an asynchronous (multithreaded) implementation."
"Oh, and if we could get instrumentation on how well the animation was played back (frame level timing would be sufficient) then that would really help our testing I suspect."
Rob later helped with some ideas about how to get this data when animations are offloaded to the GPU, "I suspect that it's not too tricky to get the data; exposing it to script may be hard. There is a GL extension (even available on some mobile devices) called GL_timer_query which lets you time the GPU pipeline. DirectX has had a similar API for a while. "
12. OVERRIDE STYLESHEETS
How are we going here?
13. INTERACTION WITH SCRIPT
I think we need to clearly specify three things regarding liveness and scripting:
If I change any of the values (e.g. the duration), do all the other timing values immediately update? (e.g. end time)
Brian: suggest yes
Shane: polyfill does
Does the target property(ies) update? e.g. does the computed style of the height I'm animating instantly update? (i.e. do we do a synchronous sample in effect) or does it lazily update? (i.e. if I query it I'll get the updated value but I won't see any change in rendering otherwise)
Brian: suggest no update until current script block completes
Shane: does that mean that if I pause() then update currentTime I _won't_ see the update? This is bad.
Brian: My thinking is yes, you won't see it until the script block finishes. Why is it bad? If we agree it's bad we could force a synchronous sample for the specific case that you change currentTime (from memory we have a number of situations like that in our SMIL code).
In a given script block is it possible for a clock tick to occur? (i.e. can the value I get back for the currentTime of the global clock at the start of a script execution block differ from the one I get back at the end of the script execution block)
Brian: suggest no
Shane: there's a specific hack in the polyfill to implement no here :-)
Next meeting: Mon Dec 10, 17:30 PST / Tues 11 Dec 12:30 AEDST / Tues 11 Dec 10:30 JST @ ???