[web-animations] Web Animations minutes, 19 / 20 Dec 2013

Web Animations minutes, 19 / 20 Dec 2013

Present: Dirk, Doug, Shane, Brian
Etherpad: https://etherpad.mozilla.org/ep/pad/view/ro.Ioh-B8wtvTy/latest

Agenda:

1. Status updates
2. Moving timing function chains to keyframes (and fixing pacing at the 
same time)
3. Fill modes and groups
4. Is step-start a special case for backwards fill?
5. Should setting currentTime = 0 while "waiting" start immediately?
6. Replace CustomEffect with a callback function


1. STATUS UPDATES
=================

Brian:
  - Lots of minor changes to spec including player algorithms

Doug
- Updating polyfill with latest renames KeyframeEffect, 
MotionPathEffect. Removed TimingInput.activeDuration
- New demos repository: web-animations-demos
- http://web-animations.github.io/web-animations-demos/

Shane
- Updating polyfill with endDelay

Steve
- Draft of Media Integration spec


2. MOVING TIMING FUNCTION CHAINS TO KEYFRAMES (AND FIXING PACING)
=================================================================
(Brian)

I did some investigation about keyPoints based on last week's discussion 
about per-keyframe easing effects and removing timing function chains.

You can see some test cases here:

   http://people.mozilla.org/~bbirtles/tests/keypoints.html

There are significant implementation differences but you can see some of 
the combinations.
(Firefox is probably not being as strict as it should and allows 
keyPoints to work with paced timing; Chrome doesn't seem to allow 
keyTimes without keyPoints etc.)

If we allow keyPoints to be in any order (the spec allows this but only 
Firefox seems to support this) then even with timing function chains we 
can't support animateMotion since animations can go back on themselves.

I would like to remove timing function chains since they introduce an 
odd dependency between the timing model and animation model where timing 
properties depend on the animation effect (so we can auto-align timing 
functions with keyframes).

At the same time, we agreed to try and make pacing separate to easing. I 
think we can break it down into three parts:

Part 1. Work out the "handles" for timing control

  For keyframe effects, the handles are the keyframes.
  For motion path effects, the handles are the points in the path UNLESS 
we have keyPoints in which case the handles are the key points (sort of, 
anyway, see below).

Part 2. Position the handles

  Three modes:
    Evenly spaced in time (distribute mode)
    Paced spacing
      (if the handles correspond to key frames and there are keyframes 
without the target value, we evenly distribute them)
      (if the handles correspond to key points, we also sort the points 
on the path between the intermediate points to maintain velocity, i.e. 
we actually pace them too but the key points determine the sequence)
    Fixed spacing
      (if there are keyframes without offsets, we evenly space them)

Part 3. Ease between the handles

(The timed-item level easing is orthogonal and is a product of using the 
supplied time fraction.)

How would this look from the model/API point of view?

Some possibilities:

A. Allow types of effect to be distinctive
  - AnimationEffect
     spacing: auto | paced(prop) | paced (type: DOMString)
  - KeyframeEffect
     offset/easing can be set on each keyframe
  - MotionPathEffect
     timingPoints: sequence<double> (aka keyPoints)
     pointTimes: sequence<double> (aka keyTimes)
     intervalEasing: <timing-function># (type: sequence<DOMString>?) 
(aka keySplines except more broad)

(Note that for all these sequences we'll have to have getters and 
setters on the interface since WebIDL doesn't allow sequence as an 
attribute type)
(Also, we'll probably end up making up separate KeyframeEffectOptions 
and MotionPathEffectOptions dictionaries for the arguments we pass in.)

B. More unified approach
  - AnimationEffect
     spacing: auto | paced(prop) | paced | <number>#
       type: (DOMString or sequence<double>)
     intervalEasing: <timing-function># (type: sequence<DOMString>?)
  - KeyframeEffect
     take offset/easing off keyframes
  - MotionPathEffect
     timingPoints: sequence<double>

I don't think this actually works though with regards to keyframes that 
need to have timing functions on a per-property basis.

So perhaps A?

 > Actually, the requirement is not to have timing functions on a 
per-property basis but on a per-keyframe basis and this is possible with 
B as well.

 > On the other hand, removing offsets from KeyframeEffect means we 
can't automatically distribute keyframes without offsets, which is a 
nice feature.

 > It also means dropping a keyframe or adding a keyframe requires 
modifying two lists

 > Hence, we think A is the best option.

 > We also think it's OK to just make these extra properties 
(timingPoints, pointTimes, intervalEasing, keyframe easing) part of the 
model for now, and expose them to the API later if there is demand for them.

Note that with either approach we remove the need for timing function 
chains. By removing 'easingTimes' from Timing/TimingInput they have the 
same members and it becomes possible again to pass a Timing object where 
a TimingInput is expected. In effect, we've moved the sequences from 
Timing to the animation effects.

 > Brian to work this into the spec


3. FILL MODES AND GROUPS
========================
(Steve)

Currently, when calculating the active time for a TimedItem with a
parent, we may consider the parent's phase. However, we consider it
only when the child is in the active phase. In this case, the child's
active time is null unless the child's fill mode 'matches' the
parent's phase. However, when the child is in the before or after
phase, we don't consider the parent's phase.

This can lead to some odd results. For example, consider a parent with
forwards fill, which is sampled after the end of its active interval,
and a child with backwards fill. The child's active time will be null
if it ends before the end of the parent's active interval (phase
after), or if it spans the end of the parent's active interval (phase
active). However, it if starts after the end of the parent's active
interval, it will be in the before phase and will have an active time
of zero. This seem inconsistent: the child's active time should be
null in this case.

I think we should always consider the parent's phase, and the the
child's active time should always be null unless its fill mode matches
the parent's phase.

I've uploaded a proposed spec change at 
https://github.com/web-animations/web-animations-spec/pull/20 which
includes a diagram which demonstrates the situation.

https://github.com/steveblock/web-animations-spec/raw/a2d0b980655d8ca63ce46fdba04d97a278919fbe/example.pdf

 > Discussed "the elf effect" where an animated character is defined to 
hammer a nail using a rotation animation. The artwork has the elf in the 
hammer-down position and the animation consists of an initial keyframe 
that raises the hammer up and a final keyframe that returns it to the 
down position. A backwards fill is used to make sure the hammer is up 
initially before the animation starts.

If this animation is part of a group that gets clipped before the elf 
animation runs, and that group has a forwards fill, I think you expect 
the elf to continue holding the hammer above his head.

This effect can be achieved using a fill mode of both (which also has 
the side-effect of making sure if the animation is cut-off part-way 
through, the partial effect is preserved, i.e. it avoids a discontinuity).

As usual, Steve is right after all.

 > Brian to make this change


4. IS step-start A SPECIAL CASE FOR BACKWARDS FILL?
===================================================
(Shane)

In particular, what should this do before the start of the animation:

   Animation(target, [{left: '0px'}, {left: '100px'}],
     {duration: 2, delay: -1, easing: 'step-middle', fill: 'both'});

because:

   Animation(target, [{left: '0px'}, {left: '100px'}],
     {duration: 2, delay: 0, easing: 'step-start', fill: 'both'});

Will have a left of 0px

 > Brian to re-visit Steve's initial patch for this issue and see if we 
can use it to achieve this behaviour where the value used in the before 
phase reflects which side of the discontinuity you are on.

(i.e. see if Steve is right, AGAIN)


5. SHOULD SETTING currentTime = 0 WHILE "WAITING" START IMMEDIATELY?
====================================================================
(Brian)

You can put a player in a "waiting" state when it has a startTime in the 
future. In this case the currentTime is 0 (this is the new "bounded" 
behavior we decided on) but the "playing" state is false.

If you call play() in such a state it begins playback immediately from zero.

But what if you set currentTime = 0 ?

There are arguments either way.

a) It should start playing immediately since setting currentTime = 0.01 
would do so and treating 0 as special creates a discontinuity.
b) It should have no effect since setting an attribute to its current 
value shouldn't have side effects.

I prefer (a) but I do wonder about this whole bounding behaviour. It 
somehow seems quite complicated.

Shane: What if the currentTime was actually -n here, <because> we're in 
the waiting state.

 > Alternative proposal: Use a combination of the playback rate and 
current time to determine if the current time progresses. Basically if 
your playback rate is positive, the currentTime only progresses so long 
as it is < length-of-media. Likewise in reverse if currentTime > 0.

 > Brian to try and prototype and spec this. Alan to tear holes in it later.


6. REPLACE CustomEffect WITH A CALLBACK FUNCTION
================================================
(Brian)

I've received feedback from a few developers that they feel like it 
would be more natural to just pass in a callback function instead of a 
CustomEffect.

That is, instead of writing:

var anim = new Animation(elem,
   {
     sample: function(time) {
       document.documentElement.currentScale = 1.0 + time * 2.0;
     }
   }, 3);

You should be able just to write:

var anim = new Animation(elem, function(time) {
     document.documentElement.currentScale = 1.0 + time * 2.0;
   }, 3);

It may be possible to specify a union of either a callback function or a 
callback interface (not sure if WebIDL allows this, I have to check), 
but I'd prefer to keep things simple and just have the callback function.

What would we lose?
a) The ability to define a clone() method on the custom effect
b) The priority field

For (a) I think there are plenty of ways of working around that. For 
example, if you want to store state in your particular custom effect 
that is particular to an element (and so should be *copied* when 
cloning), just attach it to the element. I haven't seen this ability to 
define a clone() method in other APIs so I'm assuming it hasn't come up 
as an issue elsewhere.

For (b) we already sort using the same order as for the animation stack 
(i.e. player start time, player sequence number, tree order). The 
difference is that since custom effects don't declare their target 
property this sorting takes place at a global level amongst all custom 
effects hence we added this further priority in case you need more 
control. I think it's acceptable not to provide this extra knob yet 
anyway. There are ways to tweak priority anyway.

For example, suppose you defined custom effects that manipulate scale, 
rotate, and transform and combine them in a defined order. You could 
just make them annotate their target element with their output and when 
they run they combine their result in the defined order with any 
existing annotations. Then it wouldn't matter what order they ran in.

Are there other uses cases that would be hard to realize without a 
custom effect priority?

If not, I'd rather remove the CustomEffect interface and add in the 
callback function. We can add CustomEffect later if needed.

 > Some discussion about other work arounds for (a) and (b).

For (a)--allowing per-instance state to be tracked with a callback 
function--we discussed whether the target parameter could be an 
arbitrary object but decided not to allow this yet. Closures provides 
one means of associating different targets with different functions. For 
other state, it could also be attached to the Animation object. We also 
discussed some ideas about interpolating arbitrary properties on objects 
but decided not to allow this yet.

For (b)--associating priority--we note that Function objects can have 
properties attached to them and this might be an alternative to growing 
a CustomEffect interface.

 > Replace CustomEffect with a simple callback function


Next meeting: Sometime in the week of 6 January 2014 @ 
https://etherpad.mozilla.org/yhmUQAEfpo

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

Received on Thursday, 19 December 2013 23:39:17 UTC