Server Notice:

hide

Public Pad Revision 2 Saved Dec 3, 2012

 
Meeting 34:
 
Web animations minutes, 6 / 7 December 2012
 
Etherpad:
Present:
 
Agenda:
 
1. Status update
2. Animation timelines and triggers
3. Integration with media
4. Small fry
5. Animation stack & Keyframe animations
6. The chopping block
7. Fixing Anim.duration vs Anim.timing.duration
8. Out-of-band synchronisation
9. Comparing with QML
10. Comparing with Core Animation
11. Feedback from Rob Arnold
12. Override stylesheets
13. Interaction with script
14. Adaptive duration
 
1. STATUS UPDATE
================
 
2. ANIMATION TIMELINES AND TRIGGERS
==================================
 
(Follow up after having read the appropriate docs)
 
3. INTEGRATION WITH MEDIA
========================
(Brian)
 
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
  • silently fail
  • 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
============
(Shane)
 
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.
  Proposed longification:
  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.
 
Basically, it would let you do this:
 
  elem.animate({ opacity: '0%' }, 1).onend =
     function() { elem.parentNode.removeChild(elem); };
 
Which, I think, is super duper. Or possibly zooper dooper.
 
The other remaining renaming is that I'm not really happy with KeyframesAnimationFunction. How are these:
 
(a)
    AnimationFunction -> AnimationAction
    KeyframesAnimationFunction -> KeyframesAction
    GroupAnimationFunction -> GroupAction
    PathAnimationFunction -> PathAction
    CustomAnimationFunction -> CustomAction
    Animation.animationFunction->action
(b)
    AnimationFunction -> AnimationEffect
    KeyframesAnimationFunction -> KeyframesEffect
    GroupAnimationFunction -> GroupEffect
    PathAnimationFunction -> PathEffect
    CustomAnimationFunction -> CustomEffect
    Animation.animationFunction->effect
(c)
    AnimationFunction -> AnimationEffect
    KeyframesAnimationFunction -> KeyframesAnimationEffect
    GroupAnimationFunction -> GroupAnimationEffect
    PathAnimationFunction -> PathAnimationEffect
    CustomAnimationFunction -> CustomAnimationEffect
    Animation.animationFunction->effect
    
I'm leaning towards (c).
 
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?
case A
var a = Anim(..., 2)
// @ 3 seconds
a.timing.duration = 4
 
case B
var a = Anim(..., 2)
// @ 5 seconds
a.timing.duration = 4
 
case C
var a = Anim(..., 2)
// @ 3 seconds
a.timing.iterationCount = 2
 
etc...
 
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
======================
(Brian)
 
What can we drop from v1 to make things simpler and faster to ship?
 
My (Brian's) suggestions:
  • Templates
  • 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?
  • Merging
  • 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?
  • TimingFuncCallback
  • 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
  • TimedItem.animDuration
  • Easy to calculate, but necessary?
  • TimedItem.locallyPaused
  • 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.
2012-11-29[Brian]: Added futures.html
 
7. FIXING Anim.duration VS Anim.timing.duration
=======================================
(Brian)
 
I really really don't like the fact that we have:
 
  • Anim.duration -- read-only, calculated iteration duration
  • Anim.timing.duration -- read/write, nullable, specified iteration duration
 
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).
 
Some possibilities:
 
  • 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
  • Anim.duration
  • Anim.defaultDuration
  • Anim.playbackRate
  • Anim.defaultPlaybackRate
  • Anim.fillMode
  • Anim.defaultFillMode
  • 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
  • Anim.duration
  • Anim.default.duration
  • Anim.playbackRate
  • Anim.default.playbackRate
  • etc.
  • 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.
  • Anim.duration
  • Anim.playbackRate
  • Anim.fillMode
  • 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
  • Anim.duration
  • Anim.getInitialValue('duration')
  • Anim.revertValue('duration')
  • Problem: slower, more string manipulation
 
Others?
 
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.
  • Anim.duration -> Anim.default.duration -> Anim.template.timing.duration
  • Setting Anim.default.duration overrides the template but setting it to null reverts to the template value
  • e.g. (2), if we go with D above
  • Anim.duration   is calculated from the default duration passed in (even though this   isn't publicly visible) and it is calculated from   Anim.template.timing.duration
  • Setting   Anim.duration overrides everything. Setting it to null reverts to the   template value. No way to override Anim.duration in a way that is   serialized unless we simply serialize the runtime state.
  • In the case of F, not much changes actually.
 
8. OUT-OF-BAND SYNCHRONISATION
===============================
(Brian)
 
Actually, I think this is mostly covered by the integration with media section above.
 
9. COMPARING WITH QML
======================
(Brian)
 
 
Some observations:
* Many features are similar, even the parallel and sequence animations are similar. That's good news.
 
* SpringAnimation element
   -- could be realised with the proposed SmoothTimingFunc and then add some syntactic sugar to make bouncing animations easier
   -- I've asked Pomax (author of http://processingjs.nihongoresources.com/bezierinfo/) for some help with SmoothTimingFunc.
 
* 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 :))
 
* Terminology
   -- 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
HTMLMediaElement.duration - unrestricted double - seconds
Date.getTime - milliseconds
PerformanceTiming.* - milliseconds
PerformanceNavigationTiming.* - DOMHighResTimeStamp (double) - milliseconds
 
10. COMPARING WITH CORE ANIMATION
================================
(Brian)
 
I've had a bit of a look over Core Animation to see how similar we are:
 
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:
  • s/startTime/beginTime/
  • 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
  • s/startDelay/beginOffset/
  • 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
=============================
(Brian)
 
"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
==========================
(Brian)
 
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 @ ???