w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[web-animations-2] Element.animate could allow keyframes to come from CSS declaration #2070

Open birtles opened 6 years ago

birtles commented 6 years ago

From @dvoytenko on May 25, 2017 17:24

A proposal is to extend API to accept keyframes name from a CSS style. E.g.

<script>
  element.animate('anim1', {duration: 1000});
</script>
...
<style>
@keyframes anim1 {
  from {
    transform: translateX(200px);
  }
  to {
    transform: translateX(0px);
  }
}
</style>

This would be useful because CSS syntax still provides a better way to configure keyframes than a JSON object.

Copied from original issue: w3c/web-animations#189

birtles commented 6 years ago

From @rachelnabors on May 25, 2017 18:6

Interesting thought! Questions:

  1. What if there's no anim1 in the CSS?
  2. What if there're several anim1 in the CSS?
  3. What if there's an anim1 in the JS and the CSS??
birtles commented 6 years ago

From @dvoytenko on May 25, 2017 18:30

What if there's no anim1 in the CSS?

This would be similar to referencing an absent keyframes rule in CSS animation property, e.g.

.x {
  animation: unknown 1s;
}

This would fail to do anything in CSS, so Element.animate could either fail or just return a no-op player. I'd prefer if it failed.

What if there're several anim1 in the CSS?

Similarly, I think it should work same as animation: anim1 in CSS. I believe the last one wins.

What if there's an anim1 in the JS and the CSS??

Could you explain this? Can a keyframes rule be in both JS and CSS?

birtles commented 6 years ago

From @rachelnabors on May 25, 2017 18:32

A keyframes object can be. What if you store a keyframes object as anim1. The difference between the two is one is a string and the other is a variable. I do wonder if that will lead to confusion.

I also wonder: how might this break sites? Would it result in namespacing issues for CSS?

birtles commented 6 years ago

From @dvoytenko on May 25, 2017 18:41

I see. I think it's a relatively clear API separation with Element.animate(object|array) vs Element.animate(string). Currently, the Element.animate(string) simply fails with an error like this (from Chrome):

Failed to execute 'animate' on 'Element': The provided value is not of type '(sequence<Dictionary> or Dictionary)'

So, I think enabling it here could be fairly natural. And this should be backward compatible.

RE: object vs CSS name. It's basically a difference between element.animate(anim1) where anim1 is a variable holding an array or an object as now, vs element.animate('anim1'). Not sure if this would be too confusing.

RE: namespacing. I think it'd be ok as well. Class names and other things are used freely in JS. It's pretty common for JS APIs to rely on things already in CSS styles.

birtles commented 6 years ago

From @notoriousb1t on May 25, 2017 20:23

I like the idea of defining CSS keyframes and retrieving it, but I wonder if having a mechanism for inspecting keyframes would be better.

const anim1 = document.findKeyframesByName('anim1');
const el = document.getElementById('someId')
el.animate(anim1, /* ... */);

Passing a string to an element assumes the Element has knowledge of the global environment. I think inspecting the current document for a named keyframe object of some sort might be a less coupled approach.

I have no idea what findKeyframesByName() would return in this case, but I would assume it would be a native object.

birtles commented 6 years ago

From @dvoytenko on May 25, 2017 21:44

@notoriousb1t That would definitely work. This is probably how this feature would be polyfilled :) One reason I liked string argument is because I thought it was less ambiguous, e.g. from the point of view of media queries and such. In other words, the browser would locate, extract, apply media queries, etc to keyframes rules before running the animation. Loading all this meaning into findKeyframesByName could be totally fine though.

Passing a string to an element assumes the Element has knowledge of the global environment.

Don't we assume that element.animate() is sort of a shortcut to some global APIs? E.g. as the case with Document.timeline and Animation class?

birtles commented 6 years ago

From @notoriousb1t on May 25, 2017 21:59

It is true that Animation.timeline is assigned to the current Document.timeline. I meant that it would be better to avoid relying on globals where possible. I didn't mean element doesn't currently have access to them.

birtles commented 6 years ago

From @dvoytenko on May 26, 2017 17:15

@notoriousb1t Sure. I guess if there's a robust findKeyframesByName - the problem is solved.

birtles commented 6 years ago

Just a few comments here:

.a {
  animation: abc 2s linear;
}
.b {
  animation: abc 2s ease;
}
@keyframes abc {
  from { transform: translate(0px) }
  to   { translate: translate(100px) }
}
elem.getAnimations()
    .find(anim => anim.animationName === 'abc')
    .effect
    .getKeyframes()

From there you can feed those keyframes into a new KeyframeEffect() constructor, Animation() constructor or Element.animate call.

birtles commented 6 years ago

From @dvoytenko on June 1, 2017 19:43

@birtles I can't seem to find getAnimations() implemented anywhere yet, so can't test with certainty, but it sounds like this will only return active animations?

Accepting keyframes from CSSKeyframesRule sounds like a wonderful idea though. Only one nuance I mentioned above - the onus will be on a developer to properly find this rule given CSS order and media queries.

birtles commented 6 years ago

@dvoytenko getAnimations() is implemented in Firefox Nightly and DevEdition. We're not going to ship it for a while, however, since we need to make sure the mapping between the API and CSS animations/transitions is properly specified first.

Yes, getAnimations() only returns active, filling, or yet-to-run animations.

Yes, getting to the right CSSKeyframesRule is a pain but I think that's largely because the CSSOM is so clumsy. Hopefully we can fix the CSSOM in future to make that easier.

birtles commented 6 years ago

From @dvoytenko on June 1, 2017 23:36

@birtles Ok, so if I kept track of everything right. A good way to support this request is by:

  1. Support CSSKeyframesRule as an input in place of keyframes effects in Element.animate().
  2. Improve CSSOM to make discovery/retrieval of CSSKeyframesRule easier.
Cipscis commented 8 months ago

I just ran across this issue while trying to implement an API exactly like this in one of my own projects. I'd love to see it become part of the spec.

It looks like it's been some time since the last discussion on it though, and some things that have become widely available in the past seven years may make this a bit more complicated. For example, the Shadow DOM API allows for CSS encapsulation that might mean CSS keyframes with a certain name may exist for elements only in one particular shadow DOM, or only in the light DOM.

I don't think that would be a problem for an interface like this:

Element.animate(keyframes: string, options?: number | KeyframeAnimationOptions)

But it would complicate the potential option of making it easy to discover or retrieve the available CSSKeyframesRule objects. At least, I expect that discovery would need to be done for a specific element, instead of being done directly on the document itself.