-
Notifications
You must be signed in to change notification settings - Fork 711
[scroll-animations-1] View progress contain of a sticky positioned elements on the edges #8298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
When the sticky element size is the size of the scrollport, this does seem very useful and intuitive. We would also update cover, enter and exit to take this into account as well, right? It's a bit less intuitive when the sticky element is not smaller than the scrollport and the contain range would be larger than the stuck range, however I could see it still being useful. I'm not quite sure what you mean by include a switch case with the definition for sticky. I think doing this requires that we work out the minimum and maximum sticky offset (including ancestor sticky elements in the case of nested sticky elements). This is roughly equivalent to answering what is the sticky offset when scrolled to the max scroll offset - and using that offset for the start / end positions. |
Note, this gets even more complicated if view timeline has to take into account transforms, as the sticky offset is applied based on the layout position (which doesn't include transform) so it may actually move in the opposite direction of scrolling. here's an example: https://jsbin.com/senacun/edit?html,css,output . I suspect that view timelines should operate on the layout position just like position: sticky does. I think this may already be implied by the fact that the spec already uses the principal box which is the same one used for positioning so I filed a bug for chromium's implementation which does include the transform. |
Well, if I'm not mistaken, cover doesn't have an edge case here. If the element is stuck somewhere in the scrollport then we're still during cover. Unless you mean from implementor point of view (and polyfill of course)? To make sure calculation of effective scroll length includes the stuck duration?
Yes. If the sticky element is equal in size then contain==stuck. If the element is larger then it's either still in enter, or at beginning of contain - as in And, I'd also consider adding a specific range
I simply meant to add sort of
Whoa, we were just on that page this week for implementing scroll animations, and I read the definition of |
Yes, we need to make sure that as you scroll you make progress from when the element enters the cover range to when it leaves. The current behavior would I believe re-evaluate the cover range around the current sticky position so you'd get an animation that wouldn't progress as you scroll - it would just stick at a given progress.
I've seen too many cases to count where developers using this feature stumble over the instability of a view timeline animating transform. @bramus @jh3y @argyleink may have some specific examples. I think it's better to align on a model where transform is strictly a post-layout effect (similar to what we've done with position: sticky). Also, see my above demo #8298 (comment) for how accounting for transform dramatically complicates the range of sticky position. Could you give an example of this simple parallax effect? How would it not be unstable if it is observing its transformed position and animating its own transform? E.g. here's a demo where you can observe lots of flickering in chrome canary with experimental features on due to this: https://jsbin.com/qojohiw/edit?html,css,output |
These are extra ranges that apply to the
It is currently not possible to spread one set of keyframes and match each part to a different view-timeline to track. This is the closest I got using the current possibilities but it’s not perfect, as the animation runs until Thought of using Stitching two sets of keyframes together – with one animation watching #container {
view-timeline-name: --container;
}
#sticky {
view-timeline-name: --sticky;
}
#sticky img {
animation: open 1s;
animation-timeline: auto; /* Just a value to trigger */
}
@keyframes open {
enter --container 100% {
clip-path: inset(0 50% 0 50%);
}
exit --sticky 100% {
clip-path: inset(0 0 0 0);
}
} But that might just a very tricky thing to implement. |
My proposal is that in https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges when computing the scroll position for the start / end of a range we offset the principal box by its min / max sticky position offset. This should implicitly make everything just work. E.g. imagine you have a box with top: 50px; the start scroll position for cover would be the same (when it first touches the scrollport), but the end scroll position for cover would be beyond the scrolling range because the element would never scroll out of view. |
The CSS Working Group just discussed
The full IRC log of that discussion<argyle> YehonatanDaniv: when you have a sticky positioned element and this is the subject for the view timeline, it has a top 0 so its effective stack point is both the end of the contained range and the beginning of the exit range<argyle> YehonatanDaniv: the main issue was around adding the phase of the stickiness to also be included int he contained range, so this would match expectations <argyle> YehonatanDaniv: later rob had a proposal because more issues were raised on the same issue <argyle> flackr: so when we were workign out the ranges for all these phases, we rely on the principal box <argyle> flackr: proposal is that when we try and work out the end of these ranges, we treat sticky elements as if it has it's max sticky offset, the offset that it would get at the end scrollposition <argyle> flackr: similar for the start value, or the minium sticky offset that it would have at the beginning of scroll <argyle> flackr: this makes all thephases match the visual expectation of the elements position <argyle> flackr: which has a related issue that we shouldnt be observing transforms, like other layout primitives <argyle> fantasai: i think that's a separate topic, lets take them one at a time <argyle> YehonatanDaniv: also what you wrote, that it's already in the spec, that it was implemented differently <argyle> flackr: if we dont do this, the proposal for sticky position doesnt work as well because the sticky offset isnt necessarily the same direction as the scroll if you include the transform <argyle> flackr: then thingd are worse if it includes a transform <argyle> flackr: should lwe talk about this other thing first? <argyle> fantasai: want to resolve we ignore transforms then switch back? <argyle> fantasai: proposed resolution is transforms are ignored when caclculating timeline ranges <argyle> flackr: major point of frustration, adam and bramus may talk about this as well <argyle> astearns: it's a frustration that they have to be ignored? <argyle> flackr: no, that's they're currently not ignored <argyle> bramus: result now is you get into situations where the entire thing flickers <argyle> i had to train myself out of this bug <argyle> flackr: if we could ignore the transform position, it makes devs lives easier. makes sticky position easier to reason about <argyle> astearns: hearing consensus that we ignore transforms when calculating timeline ranges <argyle> RESOLVED: ignore transforms when calculating timeline ranges <argyle> flackr: proposal for this is that the start position for view timeline uses the minimum sticky offset or the offset form the start of the scroll, and the end position uses the max sticky offset <argyle> astearns: seeing thumbs up, any concerns? <fantasai> +1 <argyle> RESOLVED: the start position for view timeline uses the minimum sticky offset or the offset form the start of the scroll, and the end position uses the max sticky offset <fantasai> Not sure how to spec it, but I think I agree with what we should *try* to spec :) <argyle> astearns: anything else on this issue? <argyle> YehonatanDaniv: probably better in a separate issue? there were other things <argyle> astearns: given fantasai's concerns about how to get it specced, let get the spec text then raise issues on that <argyle> fantasai: it's a matter of, do we have the vocab to talk about this? maybe not, and we need the spec to update to provide that <argyle> astearns: rob, can you pick something for the remaianing time? <argyle> astearns: rob, can you pick something for the remaining time? |
@flackr @ydaniv Fixed in f353d9a ; I also clarified relative/absolute positioning (in contrast with transforms/sticky). The new text reads:
If this looks good, feel free to close out the issue as Commenter Satisfied, otherwise lmk if something needs further tweaking. :) |
Thanks @fantasai! What does "offset position" refers to here? The specified inset value? This issue also aims to standardize how to treat progress of stuck boxes on the edges between ranges, so that the duration of the stuck state is appended to the progress of the earlier range. |
@fantasai I think may be a bit trickier to specify. We have to work out whether the requested percentage is before or after the point at which the sticky positioned element sticks, and then use the startmost / endmost position respectively. Consider a few cases with the common css: .sticky {
position: sticky;
top: 10px;
height: 100px;
animation-timeline: view();
animation-range: exit 0% exit 100%; /* expanded for clarity */
}
.sticky {
top: 10px;
}
.sticky {
top: -10px;
}
.sticky {
top: 0;
}
.sticky {
top: 0;
animation-range: contain 99% exit 0%;
} Cases 3 and 4 are somewhat challenging due to their ambiguity. For animation-range, we could look at whether it's the start or end of the range being asked for and use the startmost / endmost position respectively, however the same ambiguity is not easily solved for keyframes, so I'd suggest we choose a side and apply it consistently. E.g. when the range offset matches the stuck position it always uses the startmost or endmost. |
We may be able to avoid having all of this specification complexity in the spec by saying something like e.g. enter 0% is the lowest scroll progress at which the start of the untransformed principal box crosses the end of the viewport. Then we defer to the css-position spec to determine the offset of that principal box for any given scroll progress. |
@flackr I think I see what you mean. I had to implement this in JS, and kind of came with the same results - here (currenly only checks for sticky with |
I was thinking about this a bit more and I think if we define the 0% of each range as the minimum scroll progress at which the corresponding range constraint is satisfied and 100% as the maximum scroll progress at which the corresponding range constraint is satisfied that it should work for most common use cases. As an example, consider the following (demo): <style>
.scroller {
height: 500px;
overflow: auto;
border: 1px solid black;
}
.space {
outline: 2px solid rgb(0, 0, 255, 0.5);
height: 500px;
}
.sticky {
outline: 2px solid rgb(255, 0, 0, 0.5);
background: yellow;
position: sticky;
top: 0px;
height: 50px;
view-timeline: sticky;
}
</style>
<div class="scroller">
<div class="space"></div>
<div class="space">
<div class="sticky">Subject</div>
</div>
<div class="space"></div>
</div> The sticky element is unshifted until you scroll down to scrollTop = 500px, and then remains in that position until scrollTop = 950px at which point the sticky element is touching the bottom of its container. The phases would be as follows:
|
Are you sure you didn't mean: Other than that, it's 100%. |
This is a good case to dig into. scrollTop = 500 is the lowest scroll progress at which the subject's start border edge coincides with the start edge of its view progress visibility range, so per my definition above it is when it "starts exiting". However, this is a good argument that perhaps |
ok, I understand, here you deliberately put the sticky element at 1st pixel of exit. In that case it's fine. |
It's coincident with the exit, there is no border on the subject in the demo so |
ok, I took those |
@fantasai I linked a PR to try to define it simply as suggested above, would you be willing to review? |
Update: After a day of experiments I found workarounds for most of my cases, so no action needed for now (though I'd still want to tests a few things, as I still don't completely understand how Original commentI think there might be a need to be able to switch between those two ways the view progress is calculated for the sticky elements. Otherwise, it feels that there is no way to achieve the behavior that could be seen before:https://codepen.io/kizu/pen/RweORjO — here I would want the sticky elements to have the same exact animation as non-sticky ones — change when they come closer to the top of the viewport. This did work as I did expect in Chrome Canary before the version 116.0.5793.0 in which the behavior did change according to this issue. Am I missing something, and there is a way to achieve this after this change? |
Update: After a day of experiments I found workarounds for most of my cases, so no action needed for now (though I'd still want to tests a few things, as I still don't completely understand how Original commentI did try using the named view timeline on the sticky element's parent, but this doesn't work for my case, as we cannot have a wrapper element for the sticky element without breaking its sticky behavior. So, from what I can gather, it makes it impossible to somehow use the view timeline for the sticky element as if it was not sticky (as in — use it only for the sticky element's original principal box, as if it was not affected by stickiness. Why I want to have this behavior: it unlocks a lot of different use-cases, like having animated sticky headers, Sticky headers (won't be satisfied by the potential stuck state query or something, as this is not a binary state, but a scroll-driven animation): sticky-header-example.movScroll shadow (the exact case I have the video of shows a binary state, but I just didn't get to implementing the case where the shadows appear gradually; though unlike sticky shadows there is a chance it could be possible to implement this one with a different method, would need to experiment on it): scroll-shadow-example.movUpdate: Here is a working version without relying on the view timeline, so this use-case does not count — https://codepen.io/kizu/pen/LYgdgBz?editors=1100 I had experiments that did work in one of the earlier versions of Chrome Canary, and was very happy it would've been possible to achieve these cases with scroll-driven animations, so I would really want us to see if we could somehow make this possible? Update: I think, I found one workaround that is possible with the current state of the spec — using an entry range and modify it with Regardless of the workarounds I found, maybe it is time to revisit the sticky position itself, as there are just too many issues with it (related: #2496), like — could we use anchor positioning to determine what we want the sticky element be sticky to or something? This way we could use the static wrapper with the regular view timeline, and just skip it via anchoring the sticky element to the top of the scrollable container. Or maybe this is a case for reparenting? |
There's a specific, but somewhat common, use-case of using a container as a
subject
of aViewTimeline
and userange: contain
on it plus making itsticky
positioned.The problem is when this element is set to
top: 0
(many times also withheight: 100vw
), so it should practically reachcontain 100%
just as it becomes stuck, but then it should keep its position on the scrollport while the scroll continues, so it's essentially still in "contain" mode.A simple demo of the scene: https://codepen.io/ydaniv/pen/jOpBxxd
Apple's product pages are also notorious for this technique, for example:
The expected behavior is to add the duration in length of the scroll while the container is "stuck" to the total duration and extend the
contain
range to include that duration as well.The spec currently says for
contain
:So the proposed change is to include in the prose a "switch case" that includes the definition of sticky so that the effective scroll duration is extended to cover that length.
cc @fantasai @bramus @flackr
The text was updated successfully, but these errors were encountered: