Re: [csswg-drafts] [web-animations-1] Alternative to FillAnimation: replace events (#3689)

I've implemented this alternative proposal in Firefox including pretty thoroughgoing web-platform-tests. I plan to prepare a spec PR tomorrow.

The IDL as it currently stands looks like:

```webidl
enum AnimationReplaceState {
  "ok",
  "removed",
  "persisted"
};

partial interface Animation {
  readonly attribute AnimationReplaceState replaceState;
  attribute EventHandler onreplace;

  void persist ();
  void commitStyles (optional sequence<DOMString> propertiesToCommit);
};
```

Most of it is quite straight forward. The key part is that the check for replacement happens after updating the time of all timelines but before running any animation event handlers or promise callbacks (that is it happens as part of the [update animations and send events](https://drafts.csswg.org/web-animations/#update-animations-and-send-events) procedure).

That seems to work well, making the replacement predictable and producing a consistent state when script runs.

I've made it possible to call `persist()` even when the `replaceState` is `ok` and even when the animation is not finished or filling. Most of the reasons I thought of for _not_ doing that didn't hold up when I looked into it and being able to call `persist()` in advance actually proves to be useful when writing some of the tests.

I've also made it possible to call `commitStyles()` at any point in an animation's playback for largely similar reasons but also since it seems useful to decouple it from the replacing behavior so authors can manually commit and then cancel animations as needed.

Most of the outstanding questions concern the `commitStyles` function. They include:

### 1. What do we call it?

* `commitStyles`?
* `commitComputedStyles`?
* `commitResult`?
* `flatten`?

I've gone with `commitStyles` for now although I start to like `commitResult` too. Happy to bikeshed as needed.

### 2. Should `commitXXX` cause the animation to be removed?

At first it seems like it should because doing so:

* makes the removed state more useful, and
* gives the author more control over removal -- rather than waiting for auto-removal they can trigger it manually

However, I think it should _not_ because:

* It doesn't make sense for the `replaceState` member to change based on calling `commitXXX`.
  The Animation hasn't been _replaced_.
  The member would have to be renamed to something else... not sure what.

* There's no way to restore the animation from 'removed' to the 'ok' state, only to the 'persisted' state.
  That makes sense for an animation that has been replaced before but not for an animation on which one has simply called `commitXXX()`.

* It wouldn't make sense to become removed when using the version of `commitXXX` that takes a list of property names, so for consistency it should not become removed when no property names are specified.

### 3. Should it flush style?

This is really tricky.

Flushing style will produce a more accurate result. For example, if there is an animation of font-size on an ancestor, we should flush that before calculating the final computed style so that the updated font-size is reflected.

_However_, this method mutates specified style. If it also flushes style, then, for example, calling `commitXXX()` on a series of animations in the same tick will produce poor performance as it successively flushes and invalidates styles.

For now I've gone with _not_ flushing style since that seems preferably to me and is consistent with all the other methods on the `Animation` interface.

### 4. Are we sure we want to commit _computed_ styles?

I thought about this for a while and I think, _yes_, we do.

It's really tempting to try to make this method preserve the specified values in all their context-sensitive richness. For example, we could:

* Define an `interpolate(from, to, p)` function in CSS
* Making `commitXXX()` produce `interpolate()` functions as needed
* Not ship additive animation until we have suitable syntax for expressing it in static CSS (e.g. the `!add` proposal) -- since if an stack of additive animation includes context-sensitive values like CSS variables and em-based units we need to preserve the stack in order to produce the correct result.

However, not only does that still fail to cover the case of multiple animations using implicit from/to keyframes filling mid-interval (my favourite edge case it seems) it also doesn't really cover additive animations properly even once we have `!add`.

Even with `!add` you would still end up leaking memory since you'd end up appending the additive values to the inline style declaration in order to produce the desired additive effect. But the whole point of this method is that it collapses the result. The only way we can do that is to pre-combine the result, even though that necessarily means losing some information.

So I think it's probably fine to say: Use `persist()` when you need 100% fidelity, otherwise use `commitXXX()`.

### 5. What about pseudo elements?

There is no inline style we can write to for these. Perhaps that's ok?

Pseudo elements are mostly used in conjuction with CSS animations/transitions and we don't do replacement for CSS animations/transitions so this is not likely to come up often in practice.

Even if we decide that's ok, should `commitXXX()` throw when the target is a pseudo element? (And if so, should it also throw if there is no effect? No target element?)

### 6. What should it do for non-rendered targets?

Similar to the previous question, if the target is a `display:none` how should this work? Even if we can compose a suitable animation style, there is at least the problem of how to convert logical properties to physical properties in the list of properties passed to `commitXXX()` without a context to resolve them against. In my current implementation I've just made it silently fail for all these cases.

-- 
GitHub Notification of comment by birtles
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/3689#issuecomment-483147196 using your GitHub account

Received on Monday, 15 April 2019 07:56:12 UTC