- From: Kevin Doughty <socallednonflipped@gmail.com>
- Date: Sat, 21 Nov 2015 17:08:16 -0500
- To: Brian Birtles <bbirtles@mozilla.com>
- Cc: public-fx@w3.org, www-style@w3.org
- Message-ID: <CAAbwtRwrgmP=J8NoS3o=e31202rhZ8r5f6t86TG=-npw_URSXA@mail.gmail.com>
Of course the link doesn't work.
addAnimation:forKey:
On Sat, Nov 21, 2015 at 4:58 PM, Kevin Doughty <socallednonflipped@gmail.com
> wrote:
>
> https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CALayer_class/index.html#//apple_ref/occ/instm/CALayer/addAnimation:forKey
> :
>
> On Thu, Nov 19, 2015 at 2:08 AM, Brian Birtles <bbirtles@mozilla.com>
> wrote:
>
>> Hi,
>>
>> We have a difficulty in Web Animations with regards to object lifetimes
>> for animations that fill forwards. The trouble is one can do the
>> following:
>>
>> elem.addEventListener('click', evt => {
>> evt.target.animate({ opacity: [ 0, 1 ] },
>> { duration: 500, fill: 'forwards' });
>> });
>>
>> What this does, is create a new animation that fills forwards, on every
>> click.
>>
>> Now, if we call:
>>
>> elem.getAnimations();
>>
>> It should return *all* the animations we've generated.
>>
>> There are few reasons for that:
>>
>> a. You should be able to use getAnimations() to help answer the
>> question, "Why does my element have this style value?"
>>
>> i.e. This method needs to return any animation that is affecting
>> the element in question.
>>
>> b. Animations can add together so you need to keep track of all the
>> past animations anyway so you get the right final result.
>>
>> e.g. you can do the following
>>
>> // Nudge right
>> elem.addEventListener('click', evt => {
>> evt.target.animate({ transform: 'translate(50px)',
>> composite: 'add' },
>> { duration: 500, fill: 'forwards' });
>> });
>>
>> The total distance the element is translated is the sum of the fill
>> values of all animations that have run up to that point.
>>
>> All that means you really need to keep track of all the forwards-filling
>> animations. Forever.
>>
>> Why is this a new problem?
>>
>> For CSS transitions and CSS animations, once animations are no longer
>> referenced by specified style they are cancelled and no longer affect
>> the element.
>>
>> You just don't accidentally end up in a situation with millions of old
>> animations lying around. To do that, you'd have to have a *lot* of
>> elements or have an animation-name value that is very long. Either
>> case tells you to expect things to go slowly and are somewhat bounded.
>>
>> With the animation API, however, as shown in the examples above, it's
>> quite easy to get into a situation where the UA has to keep a lot of
>> Animations around without the author realizing it.
>>
>> Solutions we've discussed until now:
>>
>> a. Automatically cancel any filling animation when other filling
>> animations cover the same properties.
>> => Doesn't work for addition.
>>
>> b. Make getAnimations() only return the *last* animation that is
>> filling per-property.
>> => For the addition case, you'd still need to keep around some
>> sort of summary of the result of previous animations. Now,
>> supposing you don't expose that summary, the view you see from
>> the API is inconsistent.
>>
>> c. Expose the summary view of underlying filling animations as
>> some sort of special animation or a special attribute.
>> => This is awkward, but maybe worth revisiting?
>> e.g. elem.getFillStyle().opacity?
>>
>> d. "That's the author's problem" or "Just let UAs just do whatever
>> they feel like"
>> => This is our current position and it's not great.
>>
>> I really think we need to fix this. I don't have any brilliant ideas.
>> Revisiting (c) could work, or perhaps some sort of "animation collapsing"?
>>
>> The rough idea of "animation collapsing" is that when the UA has two or
>> more filling animations on the same properties on the same element, it
>> basically takes the fill values of the underlying animations and
>> squashes them into the property values specified on the top-most
>> filling animation. Then the underlying animation ends becoming empty
>> and can be discarded.
>>
>> (I'd really like to make it so that the underlying animation actually
>> gets cancelled and you get an event for it, but I think when it comes to
>> group effects, you want to be able to collapse individual effects
>> without having to have a complete match for all effects.)
>>
>> Adding a bit more details I think it could be something like:
>>
>> Assume:
>> * all finished animations do NOT fill forwards have been discarded
>> * Element.getAnimations() does not return Animations targetting
>> Element if the set of keyframes is empty
>>
>> For any two effects, A and B, (with corresponding animations, 'Animation
>> A', and 'Animation B') where:
>>
>> a. A and B have the same non-null target element
>> b. Animation A and Animation B are both finished
>> c. Animation A and Animation B each have a fill mode of either
>> 'forwards' or 'both'
>> d. Animation A and Animation B have the same animation type
>> e. The common animation type of Animation A and Animation B allows
>> collapsing (disallow for CSS animations/transitions)
>> f. For each animation property that is common to both A and B, there
>> is no animation effect referring to that animation property whose
>> corresponding animation has a composite order between that of A
>> and B.
>>
>> Perform the following steps:
>>
>> 1. Assume that A refers to the effect whose corresponding animation
>> has a *lower* composite order of the two.
>> 2. For each animation property, |property|, that is referenced by both
>> A and B, update each specified value |property| on B as follows:
>> i. Let |b| be the specified value of |property| on a keyframe in
>> B.
>> ii. Let |a| be the fill value of |property| on a keyframe in A.
>> iii. If the composite mode associated with |b| is 'replace',
>> let b' = |b| and composite' = 'replace'.
>> Otherwise, if the composite mode associated with |b| is 'add',
>> let b' = add(|a|, b) and composite' = the composite mode of
>> |a|.
>> Otherwise, (the composite mode associated with |b| is
>> 'acccumulate),
>> let b' = accumulate(|a|, b) and composite' = the composite
>> mode of |a|.
>> iv. Update |b| with b' and use composite' as the composite mode
>> for the keyframe.
>> (TODO: Add logic here to handle when we have multiple
>> properties on the same keyframe and we are changing the
>> composite mode. Basically, make a new keyframe in that case,
>> but then combine such keyframes at the end of the process.)
>> 3. Remove all values of |property| on keyframes in A.
>> 4. Remove any empty keyframes on A.
>>
>> It's pretty complicated and I don't love it, but I think it's a little
>> closer to the pit of success we'd like to create? If nothing else, it
>> might spark a better idea from someone else.
>>
>> Best regards,
>>
>> Brian
>>
>>
>
Received on Saturday, 21 November 2015 22:08:45 UTC