This repository has been merged into css-houdini-drafts repository of CSS-TAG Houdini Task Force.
Please file issues and send PRs there.
🚫 Old repository for AnimationWorklet specification ➡️ New repository: https://github.com/w3c/css-houdini-drafts
Home Page: https://drafts.css-houdini.org/css-animationworklet/
License: Other
This repository has been merged into css-houdini-drafts repository of CSS-TAG Houdini Task Force.
Please file issues and send PRs there.
In several places of the text, "it’s" is used instead "its".
Some use cases rely on setting different output properties on different children, some of which create a stacking context (eg. transform) and some of which don't (eg. scrollTop). It may be problematic to force a stacking context for all children when it's not strictly necessary.
We need to design an API that would allow output properties to be specified on a child-by-child basis. We'll rely in specific use cases / demos to decide if this API needs to be part of the level 1 API or can be something we could add later.
<div class="small"><img src="images/01.jpg" /></div> var smallContent = document.querySelector('.small'); new KeyframeEffect(smallContent, [{ 'opacity': 1 }, { 'opacity': 0 }], { duration: 7, fill: 'forwards', delay:5000, iterations:1 })
In the above keyframe effect, the below mentioned options are not working:
Delay
Iterations
Fill
From html page we can only call either “play()” to start the animation or “cancel” to stop and remove the animation.
How to handle “Pause” and “Finish” animations?
Also, no documentation regarding, how to pass values to “additionalTimelines_” variable of “WorkletAnimation” class.
Need your input.
Tried Browser versions:
Google Chrome - 64.0.3282.186
IE 11
IE Edge
Could the "scrollspy"-pattern be included as a motivating use case?
Demos:
https://getbootstrap.com/docs/4.0/components/scrollspy/
https://scotch.io/tutorials/build-a-custom-javascript-scrollspy-navigation
(Note: Issue was created based on feedback from Houdini Paris F2F - irc logs)
At the moment, the specification does not declare how frequent animation should be pumped and it is left up to the user agents with a recommendation to pump as fast as possible.
However, not all effects needs to be pumped as fast as possible and running them faster than necessary will lead to unnecessary power usage. Mechanisms to better optimize the frame pump rate per animation is useful.
Have animators request a particular pump rate (e.g., at least once every 4ms). Browser then tries to pump the desired rate with option to slow down if animation is missing the deadlines. Perhaps notifying the animation of rate change.
Browsers can pump as fast as possible but if no change was produced then reduce frame rate. This can be problematic for effects similar to position-sticky where they don't produce update in many frames but want to be pumped every frame.
(Note: Issue was created based on feedback from Houdini Paris F2F - irc logs)
There is interest in improving opportunities for user-agent to generate more animation frames in
parallel. Current API only allows running individual animators in parallel but there are still
potential to do more. For example it is useful to generate multiple frames for the same
animations in parallel an/or ahead of time.
If an animation is pure (i.e., has no internal state), then it is possible to
generate multiple frames for it in parallel by passing new input and recording the output.
Here are few improvements to the API that leverage this:
Introduce a mode (maybe this should be the default) where animator is assumed to be pure. We can enforce this via the same mechanisms that is used for paint worklet e.g., random assignments to global scope and dropping the animator object. (Strawman API: https://gist.github.com/anonymous/e22250dbdfa92da59508a220b8087d9d)
Enable even more effects to be written in this mode. For example if we expose scroll velocity and acceleration then a lots of existing effects don't need to have to keep state to compute it.
Browsers can also speculatively run an animator in parallel as if it is pure and then fallback to normal operation if they detect a conflict.
https://wicg.github.io/animation-worklet/#creating-worklet-animation says:
[Constructor (DOMString animatorName,
optional (AnimationEffectReadOnly or array)? effects = null,
AnimationTimeline? timeline,
optional WorkletAnimationOptions)]
interface WorkletAnimation : Animation {
readonly attribute DOMString animatorName;
};
The 2nd and 4th arugment are optional, that doesn't work because the 3rd is not. Not clear if only the 4th should be optional, or 2-4 all optional.
Paint and Layout worklets have moved to CSS namespace, CSS.paintWorklet and CSS.layoutWorklet. I think animation worklet should follow suit!
Only when on-screen? What if some JS queries the value of one of it's output properties?
Can we look to WebAnim here? Eg. @shans says that WebAnim defines that the timestamp for an animation that hasn't started yet is null
.
AnimationWorklet may fit better as a custom animation, rather than a scripted effect. This could throw on operations like reverse() since it is not idempotent (with possibility in the future to script reversing). This allows it to define its own timelines (or not), create and drive effect nodes if desired, etc. Thoughts?
e.g.
new CustomAnimation('expando', {
'expand': [elem1],
'shink': [elem2],
}, [
document.timeline,
new ScrollTimeline(options...),
]).play();
Browsers typically takes CSS animations / WebAnimations into account when computing pre-paint skirt data. But (like other JS-driven animations) there may be no opportunity to do so for animations driven by AW. The browser can't predict what the animation will look like in the future.
To fully explain the CSS animation behavior here, we should consider adding some sort of API for giving hints to the pre-paint system.
For example, in an effect where elements slide left/right onto the screen while scrolling vertically at a certain point, an AW implementation should be able to provide some sort of hint to influence when prepainting of those elements should occur.
Hopefully this is a feature we could add in the future, and just encourage people to use CSS scroll-linked animations for common cases like this today.
w3c/csswg-drafts#2432 removed AnimationEffectReadOnly from the Web Animations spec. I noticed this in web-platform-tests/wpt#9758 because Animation Worklet does partial interface AnimationEffectReadOnly
and that doesn't work.
Adapting to the changes doesn't look like a trivial search+replace, because there's also use of GroupEffectReadOnly which is only in https://drafts.csswg.org/web-animations-2/, which hasn't removed the AnimationEffectReadOnly interface...
I think WorkletAnimation would be more consistent with Web Animations if we just passed the primary timeline in the constructor and moved additional timelines to the additional options bag.
i.e.
new WorkletAnimation('foo', [new KeyframeEffect(...)], scrollTimeline, {'documentTime': document.timeline});
Then we could allow attaching to / detaching from additional timelines within the object. For example:
registerAnimator('twitter-header', class {
constructor(options) {
this.options_ = options;
}
animate(timeline, effect) {
...
if (condition) {
this.options_.documentTime.attach(this);
// Or this.attach(this.options_.documentTime);
// At this point in time the animation will now tick with the DocumentTimeline as well
// as the ScrollTimeline.
} else if (other_condition) {
// detach from document timeline.
}
}
});
It's also possible that since there is a single DocumentTimeline it may be available on the global scope to attach to / detach from.
(Note: Issue was created based on feedback from Houdini Paris F2F - irc logs)
There are a few cases where the current spec requires data to be send across thread and process boundaries:
There were some concern raised on performance/complexity impact of these cases. We should try to actually quantify such impact to better guide the working group.
In #2 we've talked of generating a warning/window.onerror (unless the developer has opted-out) whenever an animation cannot be accelerated and occurs on main. @esprehn points out that this is really not a AW-specific issue, we should solve it for CSS / WebAnim generally (although in those existing cases it may need to be opt-in rather than opt-out).
In Animation Worklet example animation-with-local-state
:
constructor(options, state) {
// |options| may be either:
// - The user provided options bag passed into the WorkletAnimation constructor on first initialization i.e, {value: 1}.
// - The object returned by |destroy| after each migration i.e. {value: 42}.
this.options_ = options;
this.state_ = state || {value: Math.random()};
}
The Worklet class constructor initialize this.state_.value
with Math.random()
random value.
But according to spec:
These specifications must require user agents to always have at least two WorkletGlobalScopes per Worklet and randomly assign a method or set of methods on a class to a particular global scope.
When two of the example worklet got initialized, each of them might get a unique value, which may cause different localTime been set, and animation may jump between two continuous localTime flow (adding 0.1 each step).
In conclusion, I think the code example doesn't meet idempotency requirement in Worklet spec.
Maybe can someone kindly explain the issue for me? Thank you!
Some (eg. @birtles) have argued that we should be consistent with animations on the web and allow any property as an output property. Eg. to avoid requiring a spec change to expand the set of animatable properties (since some browsers may move faster on that than others).
I think there's broad support for expanding the design for that, though there is debate over how exactly that should behave.
In particular, by default we'd like an error delivered to window.onerror by default whenever a not accelerated property is animated (indicating that animation can only be performed at the rate of the main thread). Optionally a developer can indicate that they are OK with main-thread animations and disable this warning (perhaps a 3rd setting for the option in #1).
Also there is significant debate over what should happen when an Animator changes an accelerated property of one element as well as a non-accelerated property of another element. There are two main options:
Proposal: have an option for now to support either mode and build concrete demos which are better in one mode over the other. We can pull the mode switch from the v1 API if no critical use cases end up requiring one of the modes.
The current proposal relies heavily on DOM APIs and CSS custom properties to send input to an animator. How would you control (play, pause, send input to, etc.) an animator from JavaScript without serializing to/from a style map? Is there an API (like element.animate
) that would give JavaScript the ability to trigger an animation without touching the style map?
@birtles argues that you may sometimes want to have an animation that is guaranteed to run in-sync with rAF on the main thread. Perhaps there should be an option on the animator for this?
Personally I'm not sure when you'd choose to use AnimationWorklet like this instead of just writing animations using rAF, but perhaps there are code sharing benefits?
Some effects rely on communicating state from one frame to the next (eg. a physics-based animation which tracks current momentum of different elements). Worklets don't permit state to be stored on the global object (see w3c/css-houdini-drafts#308). We should define an explicit API for passing state from one animate
invocation and the next for a given Animator.
For example, the spring-timing demo stores properties directly onto the AnimationProxy
.
Since all properties are now supported, the README should reflect this.
For example, in the spring example it should be possible for the Animator to somehow say "I'm done now, stop invoking my animate
function".
Maybe it should be more powerful, eg. "sleep for 5 seconds"? Or a main-thread API to resume the Animator?
https://wicg.github.io/animation-worklet/#worklet-group-effect has:
[Exposed=AnimationWorklet]
partial interface AnimationEffect {
// Intended for use inside Animation Worklet scope to drive the effect.
attribute double localTime;
};
And https://drafts.csswg.org/web-animations/#the-animationeffect-interface has:
[Exposed=Window]
interface AnimationEffect {
EffectTiming getTiming();
ComputedEffectTiming getComputedTiming();
void updateTiming(optional OptionalEffectTiming timing);
};
This combination is not valid because https://heycam.github.io/webidl/#Exposed say: "If [Exposed] appears on a partial interface or partial namespace, then the partial’s own exposure set must be a subset of the exposure set of the partial’s original interface or namespace."
In other words, [Exposed=AnimationWorklet]
needs to be added to https://drafts.csswg.org/web-animations/, or some change made in this spec.
@lukebjerring, I discovered this thanks to a failing "Partial AnimationEffect interface is exposed to 'AnimationWorklet', the original interface is not." test when reviewing web-platform-tests/wpt#12439.
From Houdini F2F discussion:
For parallelism we want to avoid using the document as a root. Perhaps if there's no root specified we should use the element itself as the implicit root? Also fix the spring example not to use the document as the root.
@dbaron says it would be helpful if the spec had an introduction describing that an implementation is free to run a worklet on either the main thread or (if all the output properties are supported off-thread) a separate thread. The idea is that this implements the same threading model as CSS animations and Web Animations (performance isolated if supported by the implementation, otherwise locked to main thread).
It should be a bit narrower in scope. Otherwise, it's going to be exposed in all other Worklets, such as the AudioWorklet
or the PaintWorklet
.
It might make sense to allow passing a single keyframe effect or an array of keyframe effects for improved consistency with animations.
i.e.
// Implicitly constructs and uses a GroupEffect because an array of effects was given.
new WorkletAnimation('foo', [new KeyframeEffect(...), new KeyframeEffect(...)], ...);
// Passes in the single keyframe effect as the effect on the animate function.
new WorkletAnimation('foo', new KeyframeEffect(...), ...);
The animators in the README use the class
keyword and static properties to construct an animation worklet. For animators that remember state across frames (#4), this seems reasonable. However, many animators will be pure transformations of input to output. For both readability/stylistic purposes and to avoid needlessly allocating memory/CPU cycles, it may be desirable to have an alternate representation for pure animators.
Perhaps:
function animator(root, children, timeline) {
// compute frame
}
animator.inputProperties = ['--scroll-position'];
animator.outputProperties = ['opacity'];
or
const animator = {
inputProperties: [
'--scroll-position',
],
outputProperties: [
'opacity',
],
animate(root, children, timeline) {
// compute frame
},
}
NOTE: The present API is also mutative - output is expressed by settings properties on the input, not by returning values. Some authors will prefer a truly pure API, but that's more a matter of style than of minimizing resource usage.
The web animation's timing model is "stateless" with a notable exception of methods which change the timing properties such as finish, or pause.
Worklet animations deviate from this timing model to enable effects that are script-driven, stateful, and runnable in a parallel worklet execution context. In particular a worklet animation output is not only dependent on its time input but also its internal state. As such the worklet animations may not necessarily be amenable to constant time seeking and direction or rate agnostic playback.
However it is desirable to make worklet animations conform to the existing Animation interface as much as possible. This include finish
method which seeks a particular time, or reverse
method and playbackRate
attribute that change playback rate.
Any methods that seeks a time or changes playback rate would cause a corresponding callback to be called on the corresponding animator in the worklet context. The user script will be responsible to provide best possible mapping for the given effect.
Here is a strawman API:
registerAnimator('foo', class {
animate(timeline, output) {
if (this.finished)
output.localTime(10);
else
output.localTime(timeline.time * rate * Math.rand());
}
finish() {
this.finished = true;
}
reverse() {
console.warn("foo animation is not reversible")
}
playbackRateChanged(rate) {
this.playbackRate = math.abs(rate);
}
}
Pros:
Cons:
We can modify the timeline.currentTime to reflect the seeked value and playbackRate.
Pros:
Cons:
There may be an elegant solution that sits somewhere between these two proposals where we do the stateless animations are automatically doing the right thing without a lot of boilerplate without it negatively affecting the stateful animation case. But I am not sure what it is. Ideas?
Even we don't find that elegant solution. I am inclined to go with idea 1 in particular because 1) I think it is appropriate for AnimationWorklet API to be slightly biased toward addressing the more complex stateful usecase 2) It provides more power on top of which we can add additional simplifying sugar in future.
"create a new animator instance" step 6 invokes an arbitrary author function through Construct without performing "prepare to run script" nor "prepare to run a callback".
From the beginning, animatorCtor
is a callback function, and callback function is not designed to be used as a constructor IIUC. "invoke" algorithm is expected to be used instead, where "prepare to run script" and "prepare to run a callback" are supported.
By the way, "run animators" looks strange, too. It pretty much looks like callback interface, but it's actually defined as a callback function and behaves differently. The difference looks to me an unnecessary confusion.
From a point of view of Web IDL, it would be natural to define animatorCtor
as a generator (not a constructor) that returns a new instance of callback interface that supports animate
operation. Then, we can call a user object's operation with the instance and operation name "animate". Or, we can define animatorCtor
as a generator that returns a callback function if callback function is preferred.
The animation starts immediately after the animation
property is set and one of it's input properties changes.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.