- From: Brian Birtles <bbirtles@mozilla.com>
- Date: Fri, 20 Dec 2013 08:38:48 +0900
- To: "public-fx@w3.org" <public-fx@w3.org>
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