Re: [web-animations] Simplifying timing groups

Hi Shane,

Two inline comments:

On Aug 8, 2013, at 2:32 PM, Shane Stephens <shans@google.com> wrote:

> Apologies in advance for the long email. This is a complex topic and I'm not sure that discussion on a mailing list does it justice. In particular, I think some wires have been crossed along the way:
>
> (1) Nobody is seriously considering "arbitrary timing functions" on Timed Items, certainly not for the first version of the specification. There is a restricted set that comprises essentially the same timing functions currently available in CSS.
>
> (2) Timing groups are functionally very similar to iterations. To argue against timing functions on Timing Groups logically implies that Timing Groups should not be allowed to iterate either.
>
> Here are the similarities:
> * Both timing functions and iterations manipulate the local time of the child animations
> * Neither (the current set of) timing functions nor iterations can introduce more than two time inversions per segment.
> * Both timing functions and iterations can cause the start/end point and child iteration points to repeat.
> * Neither timing functions nor iterations are visible from the perspective of the child when looking at local time.
>
> (3) Limited time inversions are not difficult to understand or deal with, and they produce some really nice effects (like smooth changes of direction). As mentioned above, iterations already produce inversions, and we deal with this in the model just fine. What is tricky is unbounded inversions - such as might be produced by a spring timing function. We do not intend to introduce timing functions with unbounded inversions to this version of the specification.
>
> (4) Contrary to Brian's statements, there are several animations APIs that currently provide group timing primitives of some sort. They tend to be very similar in scope and behavior to the set of timing functions we currently allow on Timing Groups. What is key about the restrictions in these APIs is not that they prevent inversions - they don't. Instead (with the exception of GSAP, see below for more detail), they prevent *unbounded* inversions.
>
> (5) Removing timing functions from Timing Groups is not a simplification. On the contrary, it significantly complicates the model, because it takes what was a unified concept that applied to both Timing Groups and Animations (the Timed Item abstraction) and forces it to be split into different abstractions for groups and animations.
>
> Three important concepts for specification simplicity are:
> (A) API/concept surface area. Removing this feature actually increases surface area, as an additional interface or concept needs to be introduced to deal with the difference between Timing Groups and Animations.
> (B) Consistency. Removing this feature introduces an inconsistency to the set of things that can be animated by Players.
> (C) Ease of use. Removing this feature makes some things that seem like they should be easy very hard to do. For a concrete example, without this feature it's impossible in the general case to take a complex animation (that might e.g. be generated programatically, or from a library of effects) and apply a simple easing to it.
>
> To the extent that this thread has discussed complexity, it is mostly talking about implementation complexity. Is it not explicitly against the principles of the W3C to choose a slightly simpler implementation at the expense of users?
>

I can not comment on each of these items in detail. I am not as conform as you both are. I do not get your argument that timing functions are similar to iterations. I think we are talking about a different problem here. It just seems that you did not comment on the fact that mapping between different timing functions is rather complex and needs some efforts to do it right. It also takes extra computation cycles, which is my (and I think Brians) concern. At the end the UA needs to guarantee that it can calculate all these mappings in time to be a real time system. And linear mapping is just the easiest and simplest way to map time lines. I can not comment on the argument about consistency in the model though.

> -----
>
> The real issue is that unbounded inversions in a timing function can lead to an unbounded number of events, and make it difficult to place those events in document time. This issue is entirely theoretical as we don't have timing functions with unbounded inversions, and because we already need to deal with inversions due to the presence of iterations. Nevertheless, I think there might be some things we could potentially look at to simplify the location of these inversions, without breaking the model. If people are interested, I'll make some suggestions in a follow up to this email.
>
> I've included some more specific responses below.
>
> On Thu, Aug 8, 2013 at 4:30 PM, Brian Birtles <bbirtles@mozilla.com> wrote:
> Hi Shane,
>
> Thanks for your response. I'll follow up below.
>
>
> (2013/08/08 13:32), Shane Stephens wrote:
> On Thu, Aug 8, 2013 at 1:25 PM, Brian Birtles <bbirtles@mozilla.com
> <mailto:bbirtles@mozilla.com>> wrote:
>     i. It means you can't convert from an animation's local time to the
>     corresponding document time since timing functions are not always
>     invertible. This introduces all sorts of complexity. For example,
>     for event handling to report both a local time and document time for
>     an event we have introduced uneased timing[1] which is not
>     well-liked and as an alternative we have considered some fairly
>     arbitrary approximations.
>
>
> I think this is a misrepresentation. Every observable animation start,
> end and iteration change has an exact corresponding document time. These
> times are in general derivable from the timing functions.
>
> I think it's reasonably complex.
>
> I'm glad you no longer believe that it can't be done :)
>
> For example, in Gecko when we evaluate cubic bezier timing functions we approximate the curve with straight-line segments to find the t for a given x. If we go in reverse where a given y may have multiple t's, we can't rely on a table of fixed chord lengths since they may obscure some crossings of a given y. I think it could be challenging to define this in a way that is consistently implemented and performant if we allow arbitrary timing functions. We could probably do something reasonable for a restricted set of timing functions though.
>
> You're talking about the impact of a specification decision on a specific implementation, rather than focusing on keeping the model simple. For an alternate data point, Blink uses recursive bezier splitting to find points on and lengths of beziers. This approach does not suffer from the shortcomings that a table of fixed chord lengths does.
>
> Also: we already have a very restricted set of timing functions. Nobody is suggesting we allow 'arbitrary timing functions', whatever they are supposed to be. The inversions in the very simple set we have are easy to characterize and will not impact performance (even with a table of fixed chord lengths).
>
> Another example of complexity is stepped timing functions where we have to be careful how we define which x to return for a given y since a single input may map to a whole range of outputs. I think the 'correct' answer will depend on a number of factors, possibly the type of event to be dispatched or the playback direction.
>
> Yes, this is something we'd need to think through.
>
> So I think, while probably solveable, it's complex enough to defer to a later stage.
>
> We shouldn't defer features because one implementation is forced to modify the way it approximates beziers. This is not the appropriate definition of complexity to use when making these decisions.

The point that was raised multiple times is that the first level does not need to address all possible use cases that authors do or may have. Instead it is important to have a consistent model and consistent implementations. For authors this is actually a lot more important than a rich and "complete" model. I also have doubts that this is an issue for one implementation. The mapping must be done in all implementations that follow the spec. It sounds reasonable to defer certain features if they can not be implemented initially. If that causes inconsistencies in the model, we have to address these.

Greetings,
Dirk

>
>
>     It also introduces some as-yet unresolved questions such, "If a
>     timing function on a group causes a child to oscillate between being
>     in and out of the active phase repeatedly, perhaps even a dozen
>     times in 100ms for a spring function, do we dispatch events for
>     every one of those transitions?" (And if we have real spring
>     functions the number of crossings would depend on how you evaluate
>     the function and could be huge.)
>
>
> If we have short iteration durations and lots of iterations we have the
> same problem.
>
> The issue is really the question about whether we should report all those starts and ends at all. Sometimes its useful to have them, sometimes its not.
>
> I can give a concrete example of this if needed but I think it's simpler if we design the model so the question never arises in the first place.
>
> Are you suggesting that neither iterations not timing functions produce intermediate start and end events? Whatever one does, the other should do.
>
>
>     iii. It complicates implementation since this feature makes it
>     difficult to decompose timing hierarchies into simple animations
>     that could be passed off to another process or even another API.
>     Without this feature, any timing hierarchy could potentially be
>     flattened down to a series of very simple sub-animations. (For
>     example, an iteration count of a group would be represented by
>     producing several sub-animations, one for each iteration. A
>     direction setting on a group could be represented by flipping the
>     playback rate on some of those sub-animations etc. On the other
>     hand, splitting a timing function on a parent into pieces, then
>     combining the appropriate piece with any timing functions already
>     applied to the child's sub-animations, is far more complex.)
>
>
> This is false. It is entirely possible to decompose timing hierarchies
> with timing functions on groups. Given that timing functions need to be
> represented in the simple sub-animations anyway, it is not much of a
> stretch at all to allow a stacked timing function to be used here.
>
> The claim was it is complex, not impossible.
>
> Stating that "without this feature, any timing hierarchy could potentially be flattened.." strongly implies that with the feature it could not.
>
> I still think it's a reasonable bit of work. Even if you don't split the timing functions but just inherit the whole list of functions along with appropriate offsets you still have to effectively apply playback rate, direction changes etc. in *in between* the functions in order to produce the same result. You could probably achieve that by modifying the offsets into the curves but it's starting to get complicated. (Then if you're interfacing onto any system that doesn't support a stack of timing functions you have to composite the functions together which is probably only possible by approximation.)
>
> I am very uncomfortable reducing the viability of the API simply because of a theoretical system which you need to interface with but that you aren't allowed to modify. In practice there are a number of approaches one could take to support animations off-main-thread, including splitting animations on animation boundaries, splitting animations on inversion boundaries (which is your proposal for iterations anyway), and simply supporting a subset of the animations model on the secondary thread.
>
>     * GSAP, which has an impressive feature set, has no 'ease' property
>     on TimelineLite / TimelineMax.
>
>
> GSAP allows you to tween both the currentTime and the timeScale on
> TimelineLite and TimelineMax, which is the same thing as a timing function.
>
> > timeScale() is just the playback rate. progress(), time() and totalTime() likewise correspond to setting the
> > playback position.
>
> > You can repeatedly call these methods to emulate a timing function but that's the not same as having timing
> > function on a group. You can do that in Web Animations too without having timing functions on groups.
>
> Actually, GSAP allows you to *tween* these values, which means you can define an automatically running animation with a timing function applied to it that modifies these values, which is functionally at least as powerful as directly applying a timing function to the Timeline.
>
> It's not even like this is an obscure feature: (a) it's part of TimelineLite, and (b) the fact that you can do it is explicitly called out in the documentation preamble for this class.
>
>
>     * WPF which has, by far, the most complicated animation API I have
>     ever seen, only has easing functions on animations.
>
>
> WPF provides at least the ability to accelerate and decelerate
> Timelines, from which both Animations and TimelineGroups inherit.
>
> That's not arbitrary timing functions. That's the same as SMIL3.
>
> ...and Web Animations!
>
> A combination of acceleration, deceleration, and auto-reversing provides almost as much power as the cubic-bezier timing function we provide. There are differences, but they are minor. Every other timing function we provide has no inversions.
>
>
>     * SMIL3, as discussed later, which is infamous for its complexity,
>     does not allow arbitrary timing functions on groups.
>
>
> And yet SMIL3 does supply auto-reversing, which is not invertible.
>
> If you model it as additional iterations with direction set, then it's invertible in the same way that iterations are invertible--that is, you have to know the current iteration but that's generally an easy requirement to satisfy.
>
> It is my strong contention that this is equally true of every timing function we currently support in Web Animations.
>
> And SMIL3 still doesn't support arbitrary timing functions on groups.
>
> What exactly are the arbitrary timing functions we support? Do you mean parameterized timing functions like cubic-bezier? If so, then SMIL3 and WPF support something that is functionally very similar.
>
>     This begs the question, do we really need this?
>
>
> If you choose your features based on what everybody else does, then
> given that 3/4 of the animations engines you supplied provide it, the
> answer is clearly yes. However, I'd prefer to analyse whether this
> feature is *useful* and proceed from there.
>
> None of those APIs allow arbitrary timing functions on groups.
>
> ... just like us.
>
> Much more importantly, all 3 APIs provide a time-varying measure of control over the flow of time in groups. If we adopt your suggestion then we won't.
>
>
>     * In general, provided the 'current iteration' of each parent is
>     known, it is possible to convert from an animation's local time to
>     document time which may be useful in a number of contexts (it may,
>     for example, be something we expose via the API in future).
>
>
> This is always possible. Note that sometimes multiple document times can
> produce the same local time. Note further that this is already true in
> the presence of iterations, without taking timing functions into account.
>
> That's why I included the condition, "provided the 'current iteration' of each parent is known."
>
> Again, I strongly contend that a similar condition is all that is required to fix the apparent problems with timing problems.
>
>     * There is no need for special overflow fill handling.
>
>
> We already perform special overflow fill handling on Animations. Why are
> TimingGroups different?
>
> It's a different mechanism. The handling for animations doesn't require any extra state to be passed around. In fact, if we were to remove arbitrary timing functions from groups this behaviour could become a property of the animations model.
>
> There is extra state required - you now need to be able to specify what happens out-of-bounds on an Animation. We had considerable discussion about this in Tokyo. Adding a similar feature to Timed Items is a natural extension.
>
>     * Media references are not required to support timing functions
>     initially. If timing functions are useful there, we can determine
>     the sort of timing functions that are readily implementable and
>     restrict the set of timing functions permitted accordingly. For
>     example, overshoot doesn't make much sense for media. Likewise, some
>     features of chained timing functions probably don't make much sense
>     for anything other than animations (like the keyframe alignment
>     feature).
>
>
> This is an orthogonal issue to your proposal.
>
> It's not orthogonal because if we have timing functions on groups we *must* be able to support easing media since we'll get inherited time that is eased, possibly multiple times. It's not clear that that's a reasonable requirement for all implementations. I'd be surprised if all platforms provide that sort of feature.
>
> All of our old drafts and meeting minutes specify that media doesn't (have to) ease, and merely respects the overall boundaries of the time slice allocated to it. This is even implemented in the polyfill. It works just fine.
>
> Additionally, media integration isn't even part of L1 any more.
>
> By removing timing functions from groups we can restrict the easing available on media to that which seems useful and implementable (which may be none initially).
>
> That sentence is equally true if you remove the part about timing functions :)
>
> It's probably clear by now that I don't agree with your reasoning. Can
> we come at this question from a use-case based analysis instead?
>
> For a start, timing functions on groups make it easy to:
> (1) keep the timing of a number of animations in sync (by applying the
> function to a ParGroup)
> (2) smear a timing function across a composite effect (for example,
> applying an ease-in across a SeqGroup containing multiple child animations)
> (3) keeping consistent timing across a complex hierarchy of ParGroups
> and SeqGroups
>
> Without timing functions on groups, how would you provide this
> functionality?
>
> There are definitely things that you can't do as easily without arbitrary timing functions on groups but I can't find a single animation engine anywhere that supports this functionality which suggests that for all current concrete use cases authors have managed fine without it.
>
> We are not talking about arbitrary timing functions, we are talking about a carefully controlled set. And in fact three of the four animations APIs discussed support controlled easing on groups.
>
> Rik points out that Flash doesn't support this and yet somehow Flash authors have managed pretty well.
>
> Rik's suggestion that Timing Groups are like Scenes leads me to believe that Flash doesn't have a concept matching Timing Groups at all, so this is hardly relevant.
>
> Note that in Flash you can group a set of elements together then animate them as a single object. When you do so, you can control the timing of the group as a single entity. With your proposed change, this might be achievable in limited circumstances (e.g. when all elements are part of a well-defined DOM subtree and you are only animating the transform), but it certainly isn't generally achievable.
>
> Last year in October when we demoed Web Animations we actually used this feature to ease both a door and its creaking sound. There are two things to note about this use case:
>
> a) It could easily be done by applying the timing function to the two child animations independently.
> b) It could *also* be done by allowing a restricted set of timing functions on groups.
>
> I have implemented smooth animation along a path that responds to keystrokes by changing the destination along that path. It is essentially impossible without timing functions on groups. Timing functions with at least one inversion are required in order to handle the case of a change of direction.
>
> I have also implemented and demoed some of the layout transitions ideas that the CSSWG has talked about. Getting smooth layout transitions with keyframes is again not impossible without timing functions on groups, but it's much harder as timing fragments would need to be computed manually, and effects split, by the user, at arbitrary points.
>
> If you want to talk about complexity, supporting these kinds of behaviors without timing functions on groups is *very* complex.
>
> I'm open to the possibility of adding restricted easing behaviour on groups like SMIL3 offers. I'd prefer to add that later but would be ok with adding it sooner if there is sufficient demand. I think that would address a number of the uses you outlined above without introducing many of the undesirable qualities of allowing arbitrary timing functions.
>
> OK, let's add the restricted set of timing functions that we already have. Nobody is asking for arbitrary timing functions.
>
> I just feel that introducing features that (a) introduce considerable complexity, (b) are provided by no other animation platform we've found yet, and (c) for which we have yet to find concrete use cases that can't be solved by other means, seems like over-engineering for a first version of a spec. I think we can add this later if needed.
>
> (a) no, the feature provides limited additional implementation complexity but actually simplifies the model
> (b) no, analogues to the feature are provided by at least 3 other animation platforms. Even Flash provides a limited version
> (c) no, I have presented two concrete use cases above and I suspect if you look at existing animation content you'll notice a lot more
>
> Sincerely,
>     -Shane Stephens
>
> Best regards,
>
> Brian
>
>

Received on Thursday, 8 August 2013 12:55:51 UTC