w3c / csswg-drafts

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

[css-scroll-snap-2] Better snap physics and customization? #8549

Open flackr opened 1 year ago

flackr commented 1 year ago

We've had requests from developers (e.g. airbnb, google search) to customize the fling curve of snap point scrolls. A common cause or desire is to align behavior, which itself seems partially motivated from the way that browsers haven't given special treatment to scrolling physics for certain snap use cases in the same way that the experiences being emulating have given them different physics.

scroll-snap-stop is often used as a simple form of control over the physics (forcing a stop at each item), but even when the typical interaction is to scroll one item at a time developers often want to still allow flings to cross multiple items.

I think expectations may vary based on the size of snap items and lengths of documents, e.g.:

While some cases the browser may be able to "just do the right thing", I think there are cases that would be helpful if the developer could give the browser a hint what kind of category things fall into.

Some possible ideas:

I'm not sure what the exact right solution is here but there seems to be a common developer need to apply different physics to different scenarios.

tabatkins commented 1 year ago

I'm pretty strongly against more arbitrary control over fling curves; every single site that does customized scrolling that doesn't match the my OS's default fling physics is deeply unsettling to interact with, personally. This probably applies to things like letting people choose a friction amount, especially since there's no intuition for how it works so it'll just be a random number that feels right to the dev at the time, and totally inconsistent across sites.

(In particular, I consider the Google Search request to be an active anti-goal. "Consistent experience across OSes and devices" is another way of saying "wrong physics on every device and OS (maybe matching the one OS/device the dev happens to use and like)". Users want physics to be consistent across different pages/apps on their device, because it's something you build muscle memory for; a single page being different from other pages, but consistent across different devices, has literally zero benefit for users and significant downsides, imo.)

But having a little more control over how far a fling is meant to go over stops sounds reasonable! Especially with nice semantic names that appear to be meaningful, like the item/page/document you have here.

Is this meant to interact with scroll-snap-stop? Like, item would be the default and give the current stop scroll-snap-stop:always behavior, while the other values loosen it a bit? Or were you thinking about something else?

flackr commented 1 year ago

I guess it depends what you mean by loosen it up a bit. I was thinking that it would be closer to the experiment I did in crbug.com/1189696 where the browser can determine more appropriate fling distances. The goal there was to make the average fling distance about a page but if the user flings fast they can still fling further. I was initially thinking this should just be the default but have since come to realize this behavior only makes sense for itemized list scrollers and not something like snap points added to spec sections.

That said, my exact proposal of changing the "average" (specific fling modification to be determined by the browser) distance would mean that you'd either use this new property (which is like a less strict scroll-snap-stop) or scroll-snap-stop but it would be interesting to think if there are ways to make the two properties work together in meaningful ways. E.g. maybe if this is like a stop at the next mandatory item after x pages of scrolling then we could do the velocity modifications in chrome similar to the crbug.com/1189696 when you're in such a scroller because we know that you only meaningfully need to differentiate between the next N snap points and not scroll further.

tabatkins commented 1 year ago

Yeah, "add friction but allow it to be overcome" should, I think, be done as part of scroll-snap-stop, which is basically "infinite friction on each item". Relaxing that to be "large but not infinite friction" sounds like a useful thing in the continuum of this property.

Maybe two axises - how often we want the "large friction" bump (every item, every page, none) and then how large the friction should be (infinite, or just large but overcomeable with a large fling).

The current always value is "every item, infinite", but "every page, infinite" sounds reasonable, as do both "every item, large" and "every page, large".

flackr commented 1 year ago

So in terms of an algorithm the UA could apply, we could scan through snap points in the scroll direction and reduce fling distance in some UA-chosen way based on this value on encountering each scroll-snap-stop point.

I am a bit worried that if we don't apply the friction uniformly it will be really hard to scroll less than that distance. For example, imagine a scroller where 5 items fit per screen. If we apply more friction to every 5th item, then it will be really hard to scroll less than 5 items. This might be fine, but it feels like it would be better to express this as those other items not having any scroll-snap-stop power. Naively, I'd want the scroll-snap-stop friction of each point to add up such that it scrolls about a page worth of items for some standard length fling.

So TLDR, I'd propose the friction of each scroll-snap-stop item be based on its proportion of the developer specified target distance.

tabatkins commented 1 year ago

Why would it be hard to scroll less than 5? What would it be different vs scrolling without any added friction at all?

I imagine it might be more difficult to scroll, say, 6 items (because you'd have to hit the momentum just right) but I think that's okay?

flackr commented 1 year ago

For illustrative purposes let's say each item is 200px on a 1000px wide scroller, and that the increased scroll friction reduces the fling distance by 4000px. This is one hypothetical way a UA may implement the friction.

This means that flinging anywhere from 801px to 5000px will land you on item 5, and to get less than item 5 you'd need to fling less than 1000px.

My alternative proposal was that having scroll-snap-stop-friction: page would result in each item applying let's say 800px of fling reduction (e.g. snap stop width / page width * 4000) so flinging 0px - 1000px lands on item 1, 1001px - 2000px lands on item 2, 2001px - 3000px lands on item 3, etc.

tabatkins commented 1 year ago

to get less than item 5 you'd need to fling less than 1000px.

Right, but that's exactly what you'd require if we weren't applying any special friction at all, too. This is why I'm confused you're saying this is a problem caused by the friction.

flackr commented 1 year ago

The difference is that we've made a large range of flings scroll exactly 5 items. So scrolling less requires a very gentle fling compared to that which gets you into the large stop at 5. This issue came up because on horizontal mandatory snap scrollers users were flinging way too far with relatively short flings. If every item has an equivalent scroll-snap-stop applied I think it would match author intentions better to make them each have a similarly large fling range that stops on those items (i.e. effectively reducing the fling distance like my prototype patch).

tabatkins commented 1 year ago

Okay, so with the assumption that normal fling velocities are tuned for long documents rather than "a page", I can see why it's reasonable to essentially increase the friction on each item so that a standard fling will advance a page worth (rather than multiple pages, as normal), and then it's easier to fling less and get individual items aligned.

argyleink commented 1 year ago

Another factor to think about is how the scroller is only allowed to have mandatory || proximity snap points. This would be like flexbox not having align-self or place-self.

I'm wondering if something like scroll-snap-point-type: mandatory || proximity, set on an item basis, would help single scrollers be more flexible. This would help snap points be more viable on a full document, for example, as it's very annoying if the scroller is set to mandatory, the user can't place the scroller in any position they want. It would be nice if certain large landmarks could individually be mandatory, making them a bit more greedy in terms of proximity and magnetizing the scroller to it while all the other snap points are light suggestions.

flackr commented 1 year ago

I was originally confused by this - because if there exist mandatory scroll snap points then presumably you have to choose one of them to snap to. I think what you're asking for is similar to increasing the proximity snap distance or an alternative mandatory snap that only looks at on screen points so that they behave like mandatory snap points while in view but otherwise do not force the scroll back to them. You can do this by making the entire "non-mandatory" region of the scroll a big mandatory snap point itself (basically what https://scroll-snap-demo.web.app/ does) but I can see how that's a bit awkward and doing so doesn't allow you to further place proximity snap points within that giant mandatory snap area.

tabatkins commented 1 year ago

Right, "mandatory" really means mandatory, and that only makes sense (under the current definitions) as a policy on the container. We could have had a different conception of mandatory that meant something like "this snap point must be snapped to if it's the nearest point to where the fling lands, regardless of the actual distance", but that would have been a different model. But what Adam is describing actually just sounds like "stronger proximity", which is definitely workable in the model, it just wasn't pursued for v1.