Open trusktr opened 9 months ago
Web Animations 2 has custom effects for this. At one point we were going to scrap them in favor of something simpler like a callback but I believe WebKit started implementing something like custom effects at one point.
This sounds like Custom Effects indeed:
const someObject = {
position: {x: 0, y: 0, z: 0}
}
const animation = new Animation();
animation.effect = new CustomEffect((progress) => {
someObject.position.x = 200 * progress;
}, 3000);
animation.play();
Could maybe be more ergonomic or could use a helper function to allow authors to be more direct and let them pass in something like { someObject.position: [{x: 0}, {x: 200}] }
.
(#) I believe WebKit started implementing something like custom effects at one point.
Available in Safari behind a feature flag.
The CustomEffect
idea lacks too much. The idea with KeyframeEffect is you get easing curves between key frames, which is very nice.
If I understand correctly, with CustomEffect
, we're entirely on our own, we have to implement key frames and easing in JavaScript.
What we need is the same APIs, but for simply applying to numbers on JS objects instead of style properties on elements. That's the only difference there should be.
CustomEffect
might still be useful if, for example, some library like GSAP wants to integrate with Web Animations while providing all of their own readings, etc.
But out of the box a Web Animations user should still be able to do everything they can with element style, just with plain numbers on any object.
The <sl-animation>
custom element from Shoelace, is fantastic!
https://shoelace.style/components/animation
Imagine <sl-animation>
being wrappable around any custom element to animate its JS properties, not only CSS properties. 😍 (and with a simple implementation due to the API ideas above)
return html`
<sl-animation duration="2000" property="position" propertyKeyframes=${[{x: 0}, {x: 100}]}>
<lume-box size="1 1 1" color="cornflowerblue"></lume-box>
</sl-animation>
`
The
CustomEffect
idea lacks too much. The idea with KeyframeEffect is you get easing curves between key frames, which is very nice.If I understand correctly, with
CustomEffect
, we're entirely on our own, we have to implement key frames and easing in JavaScript.
The idea of CustomEffect
is that you get all of the timing and animation features provided by the Web Animations API for free while using a callback-based approach to applying animation values. So, easing, delay, duration, iterations, playback rate, etc. will be accounted for to produce the progress
value provided to the callback. Keyframes however are a feature of KeyframeEffect
so would indeed not be available there.
What we need is the same APIs, but for simply applying to numbers on JS objects instead of style properties on elements. That's the only difference there should be.
That's a fair point, I think you should contribute to the CustomEffect
issue.
I’ve experimented with something like an Animator
. It used getter and setter accessors to enable declarative, implicit animation for JS objects, similar to CSS Transitions, that were triggered automatically on property value change. This feature is beyond the scope of Custom Effects and deserves more attention.
One aspect of Web Animations that is missing, and that requires people to abandon the idea of using Web Animations in favor of JavaScript libraries like Tween.js (I'm a maintainer), is that the current design allows animating only DOM element styles (unless cumbersome and non-ideal workarounds are applied).
Current problem:
Suppose we are creating WebGL-powered graphics using JavaScript. The JavaScript objects are not DOM elements, and we want to animate them.
This does not work:
Besides plain JS objects, Custom Elements are proliferating. Custom Elements can have plain JS properties too, and users may desire to animate these properties. Under the hood, a custom element's properties could map to CSS, WebGL, canvas 2D, plot or chart value, etc. Being able to animate custom element JS properties (as opposed to only styles) would be highly useful.
As a simple real life example, I work on Lume that provides custom elements for 3D, and here's a very basic animation of a number property on a Lume element:
Live example on CodePen
It would be nice to be able to use the Web Animations API for such use cases not involving style, but still highly relevant to HTML elements (custom elements). Animating the
opacity
value of the custom<lume-box>
element could look like the following:Current workarounds
Here are the current workarounds:
Don't use Web Animations API, instead use Tween.js for example. This workaround is not ideal because we want to embrace what the web gives us, instead of dropping it in favor of additional libraries.
Animate a hidden DOM element, and read properties from its computed style. The downsides of this are that it is cumbersome and hacky, plus incurs unnecessary performance cost due to cycling the browser's CSS engine and obtaining values from the element's computed styles.
Live example on CodePen
Proposal 1, keyframe effects for plain JS objects
Idea 1:
StyleKeyframeEffect
class that works howKeyframeEffect
currently works and animates the style of the given DOM element. This class extends fromKeyframeEffect
for semantics. Perhaps this class should rather (or also) directly acceptCSSStyleDeclaration
instances,CSSStyleRule
instances directly, etc, for better semantics and more possibilities (for example, animate the properties of aCSSStyleRule
in a stylesheet and it can animate multiple elements at once!).ObjectKeyframeEffect
class that extends fromKeyframeEffect
, and that accepts any JS objects, similar to what was attempted in the first example above.StyleKeyframeEffect
, but thetarget
is simply any JS object, and the properties in thekeyframes
array are plain JS number properties that correspond to the same-name properties oftarget
.KeyframeEffect
as a class that people should instantiate directly, and make it an "abstract" class that bothStyleKeyframeEffect
andObjectKeyframeEffect
extend from. For backwards compatibility,KeyframeEffect
should continue working as-is (but sites like MDN will show a warning telling people to useStyleKeyframeEffect
orObjectKeyframeEffect
, etc).The usage of
ObjectKeyframeEffect
would look like this:Idea 2:
KeyframeEffect
.Element
is detected, it works as currently.CSSStyleRule
is detected, it operates on the properties of the rule in a similar way as howKeyframeEffect
currently operates on the properties of an element's style.instanceof
), then treat it as a plain JS object, and assume that the keyframes contain objects with same-name properties and number values.This would work exactly like in the very first example above.
Being more explicit with specific classes also means that the disctinction can be more clear in non-browser runtimes. For example, a Node.js library, or Node-like JS runtime, could provide
ObjectKeyframeEffect
but notStyleKeyframeEffect
. Alternatively, a non-browser environment can also accept less types of objects if using a single class (for example, accept only JS objects, but not elements because there are no elements).Proposal 2,
Animator
Idea 1
This is a beginning exploration of an idea for animating anything, not just DOM elements.
A new
Animator
class would allow wrapping any types of objects, and the wrapper instance would have methods like.animate()
that are similar to the currentElement.animate()
.For animating plain JS objects, it could look like the following:
There'd also be similar
ElementAnimator
orStyleAnimator
classes, where for exampleStyleAnimator
could accept aCSSStyleRule
or similar.Idea 2
Similar to with Proposal 1, maybe instead of having multiple distinct classes for styles, objects, etc, a single
Animator
class can accept different types of objects and act accordingly.Being more explicit with specific classes also means that the disctinction can be more clear in non-browser runtimes. For example, a Node.js library, or Node-like JS runtime, could provide
ObjectAnimator
but notStyleAnimator
. Alternatively, a non-browser environment could also accept less types of objects if using a single class (for example, accept only JS objects, but not elements because there are no elements).Summary
I think I like Idea 2 of each of the two proposals (accepting multiple types of objects). But I'm not sure, maybe separate classes results in semantically cleaner code (include type definitions in TypeScript for example).
In any case, being able to animate plain JS objects would be not only be very useful for DOM elements in a browser, but for other cases like