Open ydaniv opened 4 months ago
I love the use cases and the direction! One thing that struck me is that perhaps the center point can be the 0 of the timeline rather than the 50%? point and everything can expand from there? Then the keyframes could have 2 values (center->out) instead of 3 (out-in-out).
Then the CSS can look like this:
@keyframes move-x {
to { translate: 50%; }
}
@keyframes move-y {
to { translate: 0 50%; }
}
.container {
pointer-timeline: --x x, --y y;
}
.figure {
animation: move-x linear auto both, move-y linear auto both;
animation-composition: replace, add;
animation-timeline: --x, --y;
/* alternatively with the anonymous timeline */
animation-timeline: pointer(x nearest), pointer(y nearest);
animation-range: cover from target 50%, cover from target 50%;
}
One thing that struck me is that perhaps the center point can be the 0 of the timeline rather than the 50%?
To keep a parallel with how ViewTimeline
works, keeping the center at 50%
makes more sense.
Also, I explored the pros and cons of the various coordinate systems in https://github.com/w3c/csswg-drafts/issues/6733#:~:text=things%20are%20named.-,Coordinate%20System,-I%20played%20a
In one of the comments there, @tabatkins said:
Since the rest of the CSS uses "0,0 is top-left of box", it would work the same way.
So there’s also a parallel to draw there.
One thing that struck me is that perhaps the center point can be the 0 of the timeline rather than the 50%? point and everything can expand from there? Then the keyframes could have 2 values (center->out) instead of 3 (out-in-out).
That's an important point! I recall why that won't work. For some effects, e.g. translation/rotation/skew, a type of [-1, 0, 1]
range is desired, and you can't get that with just 2 keyframes and a progress that goes 0->1. Unless we invent some sort of a "flipping" mechanism, but I don't think we want that. Especially if we can relatively easy reuse what we already have.
One thing that struck me is that perhaps the center point can be the 0 of the timeline rather than the 50%?
To keep a parallel with how ViewTimeline works, keeping the center at 50% makes more sense.
That's also true.
One thing that struck me is that perhaps the center point can be the 0 of the timeline rather than the 50%?
To keep a parallel with how ViewTimeline works, keeping the center at 50% makes more sense.
That's also true.
Fair points! Thanks for clarifying.
The CSS Working Group just discussed [css-animations-2][web-animations-2] (proposal) Add pointer driven animations
.
(the promised comment from the meeting)
The thing with the timelines (scroll, view, and now pointer) is that they report the progress value as a % within the defined range.
In the case of a pointer animation, if we will use this % for the element itself, or for something inside this element, we have some avenues to transform it into a coordinate for its background-position, or a nested element that will know the container's dimensions.
However, if we have an element outside, and when we lift this value via a timeline-scope, any animation applied there will be outside our target element’s context.
And we don’t have a convenient way to transform this progress value into the exact coordinates inside our timeline.
Example: if we’d want to have a tooltip that is initially positioned near a button, but then we want it to follow the cursor while the cursor is inside the button, we can’t use the button’s timeline.
Quick demo: https://codepen.io/kizu/pen/xxvGRKo (look in Chrome, as it uses scroll-driven animations and anchor positioning).
In this example, I am using an element as a substitute for a cursor, and a view timeline as a substitute for a pointer timeline (I think, in general, something like that could be useful to prototype pointer animations, as we will work on the similar terms).
While, theoretically, we could use anchor()
with a percentage for an inset-inline-start
there, it would be less performant than a transform()
, and would still be rather cumbersome.
I do not know if there is a viable solution, but I would be happy if we could somehow get not just the percentage value of some timeline, but the exact coordinate of it in its range. Getting this as a <length>
would be useful for many cases.
(note that this is not an objection for a proposal — I really like it and it will help with many cases — but rather pointing at a certain number of use cases where we need precise coordinates; I did provide some of them in a comment to a related issue — https://github.com/w3c/csswg-drafts/issues/8639#issuecomment-1484056697)
In the case of a pointer animation, if we will use this % for the element itself, or for something inside this element, we have some avenues to transform it into a coordinate for its background-position, or a nested element that will know the container's dimensions.
Well if you really wanted you could add these offsets to the range start/end.
Example: if we’d want to have a tooltip that is initially positioned near a button, but then we want it to follow the cursor while the cursor is inside the button, we can’t use the button’s timeline.
Of course you can, I'm not really following your question. Do you mean that you want it to jump next to the cursor once you hover the button and follow it, but otherwise jump back to its place?
Of course you can do that, with :hover
you can change the position, and then use an animation to transform it to the sides, and use fill: none
so it jumps back as you hover out.
Quick demo: https://codepen.io/kizu/pen/xxvGRKo (look in Chrome, as it uses scroll-driven animations and anchor positioning).
Sorry, using Chrome 128 I don't see anything happening. Plus there's no scroll there, so not clear what you intended to show ):
While, theoretically, we could use anchor() with a percentage for an inset-inline-start there, it would be less performant than a transform(), and would still be rather cumbersome.
I think my answer above still addresses this, but yeah, you could animate layout for specific cases, if that's what you wanted.
I do not know if there is a viable solution, but I would be happy if we could somehow get not just the percentage value of some timeline, but the exact coordinate of it in its range. Getting this as a
would be useful for many cases.
I think you're mixing up the model and the animated properties here. At the end the actual position has to be normalized into a progress of [0, 1]
to reason about it.
If it helps, I can link here to the library we use to "polyfill" this for users: https://github.com/wix-incubator/kuliso
Maybe it will helps web developers to reason about this better.
I do not know if there is a viable solution, but I would be happy if we could somehow get not just the percentage value of some timeline, but the exact coordinate of it in its range. Getting this as a
would be useful for many cases.
I guess just like you can't get the scrolling position from the animation, but you can specify start/end ranges with <length>
.
I guess what you're asking for is a whole different and API that currently doesn't exist.
One thing to consider regarding using root
as the source
and a non-fixpos animation target is that the timeline will always generate progress changes and update the animation.
Problem could be where even when the animation target may be outside of the viewport, the animation will still play. So that means that any animation using root
on the page will be constantly playing, and that may be unfortunate (maybe?).
Not that this is any different from any animation that plays indefinitely, but perhaps there's a way to optimize this? Can't think of an automatic one, but perhaps using animation-trigger
could help?
Proposing to start an Editor Draft for a new module pointer-animations-1
- following the example of scroll-animations-1
, since it's closely related, but does not have anything to do with scroll.
Context
Goal
Allow users to animate elements based on the position of the pointer, what is sometimes referred to as "mouse parallax". Ideally we should provide a solution for the common features (listed below) that has a coherent model and API with the existing one for scroll-driven animations.
Common features
There are common characteristics to pointer-driven animations:
[1, 0, 1]
or[-1, 0, 1]
effect progress.Solution
Add a new non-monotonic, pointer-based timeline, similar to the scroll-based one. This timeline should be linked to the position of the pointer over an element, or the entire viewport. The progress of the timeline is linked to the position of the pointer from the start edge to the end edge of the source element or the viewport.
Prior art
This previous proposal by @bramus with more elaborate details, that are also relevant here, which relied on exposing a new pseudo-class for hovered element, plus exposing new environment variables for the position of the pointer.
JS implementations
Some libraries that allow this effect: Parallax.js, Tilt.js, and Atropos.js.
Live examples
Concept and terminology
Timeline
A new timeline that's linked to the position of the pointer, relative to an element/viewport - let's call it "source" - on a specific axis, either
x
ory
. Initially the timeline is defined by the source, starting at its start edge and uniformly increasing to its end edge.Like ViewTimeline, the PointerTimeline is linked to the un-transformed layout box of the source (so that a timeline on the same element that's animated with transforms doesn't change with the animation).
Attachment range's centering (center shift)
It's common for pointer-driven animations to shift the center of the attachment range to a specific point, so that common animations with an effect progress of
[1, 0, 1]
or[-1, 0, 1]
always reach0
on that specified point. Usually that point is relative to the animated element - let's call it "target" - rather than its source. Usually it's the target's center.To achieve that, we also need to a way for authors to define that shift of the timeline's center to a specified point, either on the source, or on the target. The important thing to note here is that while the range is defined relative to the source, the shift of the range's center may be defined relative to the target.
Ranges
The timeline can then be expanded/contracted or stretched/squeezed using ranges. These are also controlled in a similar fashion to ranges of ViewTimeline, but with some adjustments. The available ranges are:
cover
,contain
,fill
, andfit
- building on top of known keywords ofobject-fit
- though it seems havingnone
for a range feels awkward, so currently it's replaced withfit
.All these ranges produce the same identical timeline if the range's center is at the source's center, i.e. center is not shifted. However, if the range's center is shifted, the ranges behave differently and produce different timelines.
Cover
This range acts similar to radial-gradient's
farthest-side
keyword. The attachment range reaches either 0% or 100% at the farther edge of the source, and then mirrored to the other side from range's center, so that the attachment range is always covering the source.Example with center shifted to target's center:
Contain
This range acts similar to radial-gradient's
closest-side
keyword. The attachment range reaches either 0% or 100% at the closer edge of the source, and then mirrored to the other side from range's center, so that the attachment range is always contained within the source.Example with center shifted to target's center:
Fill
This range acts similar to the
object-fit
'sfill
keyword. The attachment range reaches 0% at the start edge of the source, and 100% at the end edge, so that it's stretched to fill the source from its center outwards. In practice this is equal to automatically setcover
to the farthest edge andcontain
to the closest edge.Example with center shifted to target's center:
Fit
This range acts similar to the
object-fit: none
keyword. The attachment range reaches 0% at the start edge of the source, and 100% at the end edge, and maintains this size even if its center is shifted, so that it's simply displaced according to the center shift.Example with center shifted to target's center:
Transitioned progress
It's also very common to see pointer-driven animations that have a "lerp" effect or a time-based transition on the effect's progress, so that it slightly lags behind the pointer position. This is usually done with a
transition
on the animated properties or by an interpolation on every frame between the current progress and the previous one.This was suggested for scroll-driven animations in #7059, but was deferred to level 2. Since it's a common pattern for pointer-driven animations, it could be a good opportunity to introduce it here.
Velocity
Some effects are linked to the velocity of the pointer, rather than its position. This is also common for scroll-driven animations, but was deferred to level 2. Mouse events already expose the delta between previous and current position via
movementX
andmovementY
, so it could be a chance to build on that and introduce that as well.Polar Axes
Some effects are linked to the polar coordinates of the pointer, rather than its cartesian ones. While it could be very useful to add a "distance" and an "angle" axes to the proposal, they get very complex when trying to solve their progress and ranges with the proposed model. So it's probably best to defer them to further iterations, or to level 2 entirely.
Proposal
CSS
Add a new property
pointer-timeline
that takes adahsed-ident
asname
and a one ofx
ory
asaxis
.For the anonymous timeline, a
pointer()
function that takes a source keyword and an axis keyword should be added as value foranimation-timeline
. Possible values forsource
are:self
for same element,nearest
for nearest containing block , androot
for viewport.The
animation-range
should be extended to include the new range names:fill
andfit
.In order to allow the attachment range's center shift, a new property
animation-range-center
should be added, that takes a<length-percentage>
value and an optional keywordtarget
. Without thetarget
keyword, the value is relative to the source, otherwise it's relative to the target. Inside theanimation-range
shorthand this value can either be introduced following anat
, or a/
.Example:
Web Animations API
Expose a new interface
PointerTimeline
:Add a new attribute
rangeCenter
toAnimation
of the following type:Example:
Edit: My examples were off 😖 fixed the transforms.