W3C home > Mailing lists > Public > public-fx@w3.org > October to December 2015

Re: [web-animations] Freeing up forwards-filling Animation objects

From: Kevin Doughty <socallednonflipped@gmail.com>
Date: Sat, 21 Nov 2015 17:08:16 -0500
Message-ID: <CAAbwtRwrgmP=J8NoS3o=e31202rhZ8r5f6t86TG=-npw_URSXA@mail.gmail.com>
To: Brian Birtles <bbirtles@mozilla.com>
Cc: public-fx@w3.org, www-style@w3.org
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:48 UTC

This archive was generated by hypermail 2.3.1 : Saturday, 21 November 2015 22:08:48 UTC