Re: [csswg-drafts] [css + web-animations] Handle getKeyframes for CSSAnimations (#5331)

> What does keyframe-creation-time refer to here?

In css-animations-2, the spec says "[...] keyframe objects are generated as follows". By keyframe-creation-time, I meant whenever _that_ keyframe object generation is taking place, which should be during the style resolve which "discovers" that a valid animation was specified in CSS. This handled somewhat by css-animations-1 ([Animations chapter](https://drafts.csswg.org/css-animations-1/#animations)), although it deals mostly with Animation objects, _implying_ the creation of keyframe objects when animations are started/updated.

Previously we'd generate these keyframe objects right off the bat to something that resembles _computed_ keyframes. My proposal here is that we instead generate something closer to _specfied_ keyframes, and then lean on [Calculating computed keyframes](https://drafts.csswg.org/web-animations-1/#calculating-computed-keyframes) for the rest.

> pending substitution value [...] making round-tripping even less likely to succeed

The keyframe objects generated in my proposal here are not directly observable as-is, since we're also specifying that `getKeyframes` returns _computed keyframes_ (for keyframes generated by CSS). In other words, the declarations in the generated keyframes will undergo [Computing properties values](https://drafts.csswg.org/web-animations-1/#computing-property-values) before exiting through `getKeyframes`. This means that any pending substitution values will have been eliminated before they are observed.

For example:

```
  :root {
    --x: 1px 2px 3px 4px;
    --y: 5px 6px 7px 8px;
  }

  @keyframes test1 {
    from { margin: var(--x); }
    to { margin: var(--y); }
  }
```

The keyframes object we'd generate from the above can be illustrated by:

```
  @keyframes test1 {
    from {
      margin-top: <pending-substitution: var(--x)>;
      margin-right: <pending-substitution: var(--x)>;
      margin-bottom: <pending-substitution: var(--x)>;
      margin-left: <pending-substitution: var(--x)>;
    }
    to {
      margin-top: <pending-substitution: var(--y)>;
      margin-right: <pending-substitution: var(--y)>;
      margin-bottom: <pending-substitution: var(--y)>;
      margin-left: <pending-substitution: var(--y)>;
    }
  }
```

(Note that pending-substitution values implicitly capture the shorthand it originates from, such that we know how to parse the value after substitution).

Then, when calling `getKeyframes`, we calculate and return the computed keyframes, which can be illustrated by:

```
  @keyframes test1 {
    from {
      margin-top: 1px;
      margin-right: 2px;
      margin-bottom: 3px;
      margin-left: 4px;
    }
    to {
      margin-top: 5px;
      margin-right: 6px;
      margin-bottom: 7px;
      margin-left: 8px;
    }
  }
```

This round-trips at least somewhat successfully (though it's no longer responsive to changes in `--x`/`--y`, of course).


If you had instead included `--x` in the keyframes itself:

```
  @keyframes test2 {
    from { margin: var(--x); --x: 1px 2px 3px 4px;}
    to { margin: var(--x); --x: 5px 6px 7px 8px; }
  }
```

We can illustrate the generated keyframes as:

```
  @keyframes test2 {
    from {
      margin-top: <pending-substitution: var(--x)>;
      margin-right: <pending-substitution: var(--x)>;
      margin-bottom: <pending-substitution: var(--x)>;
      margin-left: <pending-substitution: var(--x)>;
      --x: 1px 2px 3px 4px;
    }
    to {
      margin-top: <pending-substitution: var(--x)>;
      margin-right: <pending-substitution: var(--x)>;
      margin-bottom: <pending-substitution: var(--x)>;
      margin-left: <pending-substitution: var(--x)>;
      --x: 5px 6px 7px 8px;
    }
  }
```

And then the computed keyframes (and hence the result from `getKeyframes`) would depend on the current time of the animation. At 50%, the value of `--x` would just have flipped to the value specified by the `to` frame, so we get computed keyframes (again using CSS syntax for illustration purposes):

```
  @keyframes test2 {
    from {
      margin-top: 5px;
      margin-right: 6px;
      margin-bottom: 7px;
      margin-left: 8px;
      --x: 1px 2px 3px 4px;
    }
    to {
      margin-top: 5px;
      margin-right: 6px;
      margin-bottom: 7px;
      margin-left: 8px;
      --x: 5px 6px 7px 8px;
    }
  }
```

This (and similar situations with complicated dependencies) will only round-trip correctly for the current point in time, but we already discussed this in #5125. But it won't fail "catastrophically" like it sounds like you were thinking.

>> When computing the keyframe, a var() reference will substitute whatever the computed value of the referenced custom property currently is, not to the value locally defined by the same keyframe.

> I'm not sure if that's the behavior we want for CSS keyframes is it?

The above question was imported from [elsewhere](https://github.com/web-platform-tests/wpt/pull/24655#issuecomment-661091458), but I'll reply here since it pertains to this spec edit.

This is exactly what #5125 was about: how (and _when_) to resolve dependencies. The bullet point I'm removing here ("All variable references are resolved to their current values") is presicely the thing that prompted that issue in the first place, since it was not clear (to many people) what this meant or how it was supposed to work.

I'm really only effectuating what we already concluded with in #5125, namely that dependencies behave according to the G-β behavior. In practice this means that keyframes that are generated by CSS keep `var()` references around, and the substitution of these are delayed until computed-keyframe-time. (And how to deal with these dependencies at computed-keyframe-time is now reasonably well defined as of recently). This gives us a nice consistent model for how dependencies are resolved, I think.

Sure, we _could_ define some special rules, and resolve the `var()`s against _something_ at the time keyframes are generated. Then there would be no `var()`s left to resolve by the time [Computing properties values](https://drafts.csswg.org/web-animations-1/#computing-property-values) is invoked. But this would effectively re-open #5125, as it sounds like a violation of G-β. And at this point I don't see why we should go looking for more trouble, when the model we already concluded with in #5125 seems to work quite well. :-)


-- 
GitHub Notification of comment by andruud
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/pull/5331#issuecomment-661793758 using your GitHub account

Received on Tuesday, 21 July 2020 11:14:27 UTC