[web-animations] Web Animations minutes, 1 / 2 Aug 2013

Web Animations minutes, 1 / 2 Aug 2013

Present: Doug, Jack, Shane, Brian
Etherpad: https://etherpad.mozilla.org/ep/pad/view/ro.SGDKDYlm3gy/latest


1. Status updates
2. Making players stop
3. Independent animation of transform components
4. Renaming 'offset'
5. Additive/seamless/perfect animations


  - Fill mode diagram: 
    > Brian to send to public-fx along with preferred 'auto' approach


Previous discussion: 
http://lists.w3.org/Archives/Public/public-fx/2013JulSep/0009.html, point 6

Recap: Any new ideas? We have a model that seems reasonable but Brian 
was concerned about the play head getting stuck when the source content 

Doug: Stuck beyond the end of the source?

For example, if the source content is 1min, and the player reaches the 
end, then the application updates the source content to 45s, the current 
time will be outside the range where it updates. So what happens if you 

Probably the best thing is update the behaviour of reverse() so it jumps 
to the end if its past it. (Likewise if the current time is < 0 and 
playbackRate < 0 and reverse() is called, jump to start.)

While we're at it, we probably also want cancel() since setting 'source 
= null' is not obvious. How about play() and pause()?

If we add pause() we should make the paused attribute readonly. This 
would actually bring us into line with HTMLMediaElement a little more.

 > Brian to incorporate changes


Sometimes animators want to independently animate rotate/scale etc, for 
example in order to use different timing functions.


See comments in:

Currently we can acheive this somewhat by using composite: 'add', but we 
can never 'set' a single component independently without affecting other 

(Brian: Further issue is that the order of addition depends on start 
time which seems problematic. You make the scale start before the 
translate and suddenly the result is different?)

Shane: There are two ways we can emulate this.

Doug: It may be possible to support this by adding special properties 
that composite independently but are then combined with the composited 
output for the transform property.

Something like:
transform: [rotate-stack] [translate-stack] [scale-stack] ... [regular 
transform list-stack]

But this might be odd:
element.animate({transform: 'rotate(30deg)'});
element.animate({rotate: '90deg'});
=> transform: rotate(90deg) rotate(30deg)

Does this only affect transform? Are ther other properties which would 
benefit from components being animated independently?

If you set transform to 'rotate(0deg) scale(1) translate(0px)' this has 
no immediate effect, but it changes what happens when you subsequently set:

rotate: 90deg
translate: 100px

new Animation(target, {transform: 'translate(100px)'}, 1)
new Animation(target, {rotate: '90deg', composite: 'add'}, 1)

should act the same as

new Animation(target, {transform: 'translate(100px)'}, 1)
new Animation(target, {transform: 'rotate(90deg)', composite: 'add'}, 1)


new Animation(target, {translate: '100px'}, 1)
new Animation(target, {rotate: '90deg'}, 1)

probably should behave differently to

new Animation(target, {transform: 'translate(100px)'}, 1)
new Animation(target, {transform: 'rotate(90deg)'}, 1)

Possible behaviours:

1) Accumulate all independent transform components ('rotate', 
'translate' etc.) and aggregate transforms independently and then at the 
end when you apply the transform to the target alement you combine them 
as follows:
   a) First you combine the 'translate', 'rotate' etc. into a single 
matrix in effect
   b) Then you combine the component-based transform with the aggregate 
     x) in some set order (pre or post multiply the matrices)
     y) add/replace the components with the fixed-order decomposition of 
the aggregate transform
For (x) we could clarify the relationship between component and 
aggregate through naming, e.g. finalRotation, initialRotation etc.

AI: Shane to flesh out some of the problems he sees with this approach

2) Insert component transforms and aggregate transforms into the 
compositing stack in the normal order. When compositing:
   a) component transforms on component transforms: treat each channel 
seperately and output a component transform
   b) aggregate transforms on component transforms: convert the 
component transform to an aggregate transform then composite as 
currently defined, outputting an aggregate transform
   c) aggregate transforms on aggregate transforms: composite as 
currently defined, outputting an aggregate transform
   d) component transforms on aggregate transforms: decompose the 
aggregate transform into component channels, then treat each channel 
seperately and output a component transform. However, composite: replace 
overides all the channels.

Jack's augmentation: decompose / recompose based on the actual order in 
the tranfsorms.

Brian's concerns:
  - order-dependency between components and aggregates seems brittle
  - can we get this into CSS Transforms?

AI: Shane to build a simulation for further exploration.


{ "transform[0]": 'rotate(30deg)' }

Building block, hard to use otherwise (many failure cases?):
target.animate({ "transform[0]": 'scale(2)' }
target.animate({ "transform[0]": 'rotate(30deg)', composite: 'add'}
INPUT: 'translate(20px) rotate(0deg)'
OUTPUT 1: 'scale(2) rotate(0deg)'
OUTPUT 2: 'scale(2) rotate(30deg) rotate(0deg)'

Ordering is TSR
Animation(target, {transform: 'translate(0px) scale(1) rotate(0deg)'}, 1);

Animation(target, {'transform[0]': 'translate(100px)'}, 1);

 > For the time being, Shane will prototype 1 and 2 and seek feedback.



Initial reaction: timeOffset is long for something you type all the time.

Observation: You don't type it all the time. For many common cases the 
auto-distribution behaviour is suitable or you have a to-animation.

What does CSS do? It just talks about percentages of the animation duration.


   elem.animate([{ top: '100px' }, { top: '200px', timeOffset: 0.6 }, { 
top: '100px' }], ...);

I wonder if keyframeOffset is better? keytime? keyTime? keyTime has the 
nice property that it lines up with the SVG attribute that serves the 
same purpose 'keyTimes'.

Shane: I empathise with the confusion (offset could be a length thing or 
a time thing) but I don't think there's a strong need to change.

If we change, I'm not sure about keyTime as I don't think key identifies 
strongly with keyframe. keyframeOffset, keyframeTime, timeOffset all 
seem OK. Or even just keyframe?

 > Mark as issue in spec: possible candidates timeOffset, keyTime



We can fundamentally support animation from -delta to 0. One valid point 
that Kevin brings up is the relative timing of setting the position of 
something and having an animation from Web Animations apply:

 > Thank you for considering the additive animation usage pattern that I
 > call "seamless" and for mentioning the "perfect" timing function at
 > the last meeting, even if it was as a concern. I would like to
 > reiterate its importance, intended for heavy layout of things like an
 > isotope.js grid or animating editable text, and introduce a new
 > keyframe animation technique. To the uninitiated, the underlying
 > value is instantly changed and an additive negative delta animation
 > with no fill is added. This moves the element back to where it came
 > from then animates to zero. It allows for many concurrent animations
 > and is in my opinion the best pattern for responding to user
 > interaction. My biggest concerns with the spec are there is no
 > guarantee that adding animations can happen at the same time as
 > setting the underlying value, and the intended use has final state
 > determined by a forward filling animation instead of the underlying
 > value.

I think this means if we do:

   // Animate to 100px and stay there
   var underlyingValue = parseInt(rect.getAttribute("x"));
   rect.setAttribute("x", "100");
   rect.animate({ x: [ -underlyingValue, 0 ], additive: "sum" }, { 
iterationDuration: 1, fill: "none" });


   var underlyingValue = parseLength(rect.style.left);
   rect.style.left = '100px';
   rect.animate({ x: [-underlyingValue, 0], additive: 'sum'}, ...});

Then we might render the rect with x=100 before the animation gets a 
chance to start.

What to do?

Discussed the concern with the final state of an element being 
determined by the animated value and agreed there are many use cases 
where it is legitimate to want to set the underlying value and then 
animate "over the top" to reach that value.

Shane: One possibility is that playNow() implies synchronous sampling of 
the model and immediate animation state updating, whereas play() 'plays 
nice' with CSS by deferring animation start to be part of style 

Or something similar.

Basically, we do need some implementation requirements so that authors 
can be sure that if they set underlying style/values and apply an 
animation to start immediately that there is no "gap" where we render 
with the updated style/values but not the animation.

<aside author="Shane">

There's another way of dealing with this particular use case, which is 
to have a single non-additive animation and 'slice' it using a par 
group. I've done this very successfully, but it isn't obvious that it's 
a fruitful approach. For example:

var xPosition = new Animation(target, {x : [0px, 1000px]}, 1);
// move element to b:
var player = undefined;
function moveElement(b, dur) {
     if (player) {
         var a = player.source.children[0].localTime;
         player.source = null;
     } else {
         var a = 0;
     var par = new ParGroup([xPosition],
         {iterationStart: a/1000, iterationCount (b-a)/1000,
          playbackRate: (b - a) / dur});
     player = document.timeline.play(par);

By controlling the timing function you can get smooth interchanges. You 
can't use the same timing functions that Kevin suggests, however.

I think this is actually a better way of supporting Kevin's use case as 
it provides considerably more lattitude in shaping the way elements 
react to changes in direction.


 > The API uses element.style in its implementation, which gave me some
 > difficulty as I wanted to use it as well. The suggestion was to use
 > document.styleSheets[].rules[].style, which I find awkward. However,
 > the real problem is neither could be synced with added animations. I
 > attempted an alternate approach, using a non-additive animation that
 > fakes the setting of the underlying value. The API allows animations
 > to have the same start time, unfortunately there is still flickering
 > which I suspect is a redraw between calls to replace() and play().

I think Kevin's saying that he couldn't get style setting and animation 
starting to sync up. I'm not surprised! I've managed to hide style 
setting behind animations, but this is:
(a) pretty hard
(b) assumes the animation is going to start at 0.

Shane will look into fixing this in the polyfill.

 > The additive pattern received some criticism, in particular the
 > requirement that it use cubic-bezier(0.5, 0.0, 0.5, 1.0) to hide the
 > seams. To-animations are very impressive, but I have to point out it
 > is impossible to blend timing functions when performing path
 > smoothing of animations that start at different times. For example,
 > consider something like cubic-bezier(1.0, 0.0, 1.0, 1.0), which I
 > call "crazy ease in". This is probably not desirable for layout of my
 > expected use case anyway. While unrelated to timing functions, a
 > drawback of to-animations are that instant changes to the underlying
 > value are not visible while animations are running.

I don't understand this. With regards to the last sentence, changes to 
the underlying value are visible while the animation is running but they 
get progressively dampened.

Further clarification required here.

Will following up remaining feedback next time.

Next meeting: Thurs Aug 8 18:00 PDT / Fri 9 Aug 11:00 AEST / Fri 9 Aug 
10:00 JST @ https://etherpad.mozilla.org/ZrAl5alJi0

Past meetings: http://www.w3.org/Graphics/fx/wiki/Web_Animations/Meetings

Received on Friday, 2 August 2013 05:43:04 UTC