- From: fantasai <fantasai.lists@inkedblade.net>
- Date: Wed, 22 Jul 2015 15:10:30 -0400
- To: "www-style@w3.org" <www-style@w3.org>
I want to start off by saying that I think scroll snapping is a great idea, and I fully support exposing this functionality through CSS. However, as roc has mentioned on the mailing list: https://lists.w3.org/Archives/Public/www-style/2013Aug/0224.html https://wiki.mozilla.org/Gecko:CSSScrollSnapping and as I've mentioned at multiple F2Fs: https://lists.w3.org/Archives/Public/www-style/2014Feb/0607.html https://lists.w3.org/Archives/Public/www-style/2015Jan/0009.html https://lists.w3.org/Archives/Public/www-style/2015May/0282.html a major weakness of the current proposal is the way it conceives snapping on a coordinate model rather than a box model. This requires a lot of manual calculations in figuring out the correct coordinates *from* the box model; and also makes sensible scroll-snap settings dependent on the relative sizes of the viewport and the snappable contents, causing problems for users are unexpectedly large and/or small screens (a problem commonly ignored by many authors). I'd prefer to build off of roc's model (see wiki above), and to that end I'd like to point out the two major use cases that have been presented, and a third that's been mentioned: Use Case A: Snapping to the start or middle of each box e.g. address book (start) or photo album (middle) Use Case B: Snapping to the start or middle of a group of boxes, where the number of boxes depends on how many fit in the viewport e.g. scrolling image galleries Use Case C: Snapping to boxes (or points) in 2D e.g. on a map, where you want to snap points of interest to the center, or a flow-chart diagram, where you want to snap the edges of each box into the visible area. In both cases, you don't want objects wholly outside the visible area to influence snapping And also the key question of What happens when the viewport is significantly larger or smaller than one item? Whose answer I would like to be The user still has a pleasant experience despite the author not caring. The Proposal ============ Properties set on descendants of the scroll container: scroll-snap-edges: <box> || <length>{1,4} scroll-snap-align: none | group | <alignment> scroll-snap-scope: infinite | finite Properties set on the scroll container: scroll-snap-type: none | proximity | mandatory scroll-group-align: <alignment> Where <alignment> = [ start | end | edges | center ]{1,2} <box> = border-box | margin-box Initial values are as follows: scroll-snap-area: border-box; /* Use edges of border box */ scroll-snap-align: none; /* No snapping of this box */ scroll-snap-scope: infinite; /* Snap positions extend infinitely, creating a grid of snap lines */ scroll-snap-type: none; /* No snapping in this scroller */ scroll-group-align: center; /* Snapped groups are centered */ Overview of Behavior ==================== scroll-snap-align - Specifies the element's snap position as an alignment of its box within the viewport. If the 'group' value is used, it is group-snapped. scroll-snap-area - Specifies the box that is used for snapping. Defaults to border-box; can also specify margin-box and/or use offsets. scroll-snap-scope - Infinite causes snap edges to extend infinitely across the scrollable area. Finite limits the snap edges to the actual edges specified, so that e.g. boxes wholly outside the viewport don't influence snapping behavior. (Infinite is the initial value because for single-axis scrolling or gridded layouts, it doesn't matter, and this is probably the more performant option for UAs.) scroll-group-align - Collects all snapping edges of group-snapped boxes, segments them into groups that will fit within the viewport, then creates snap areas that capture the specified alignment of each such group. (Note that such areas may overlap, if group-snapped boxes are arranged in an overlapping pattern.) scroll-snap-type - Tells the scroller to pay attention to snapping. (There were some issues raised on this design, but just copying the MS proposal for now.) Examples ======== Use Case A: 1. Snapping to 0.25rem above the top of each heading :root { scroll-snap-type: proximity; } h1, h2, h3, h4, h5, h6 { scroll-snap-align: start; scroll-snap-area: 0.25em; } 2. Snapping to the center of each photo :root { scroll-snap-type: mandatory; } img { scroll-snap-align: center; } Use Case B: 1. Snapping to the top of each "page" of address book entries in a list of entries :root { scroll-snap-type: proximity; scroll-group-align: start; } article { scroll-snap-align: group; } Use Case C: 1. Snapping each flow chart entry to within the viewport when it falls near the edge: :root { scroll-snap-type: proximity; } li { scroll-snap-align: edges; scroll-snap-scope: finite; } 2. Snapping each city on a map to the center of the viewport, but only once it gets near the center in both dimensions: :root { scroll-snap-type: proximity; } city { scroll-snap-align: center; scroll-snap-scope: finite; } Handling Small Viewports ======================== The snapped position of a box is given by its scroll-snap-align property. This is a simple mapping to the current model. However, if the scroll-snap-area is larger than the viewport... * Inertial scrolling (and scroll-adjustment caused by snapping) continues to align to the snap-point as normal. * For explicit/programmatic (non-fling) scrolling: * While the area fully fills the viewport in a given axis, snapping is ignored in that axis: the container can be scrolled arbitrarily and will not react. * If the container is scrolled such that the area no longer fully fills the viewport in an axis, the area acts as if it has both-edges snapping in that axis, resisting outward scrolling until you fling out or pull it sufficiently to trigger snapping to a different snap-point (with either proximity or mandatory behavior as appropriate). As an example, imagine a photo as the area, or a slide in a slideshow. You want mandatory snapping from item to item, but if the item happens to be larger than your viewport, you want to be able to scroll around the whole thing once you're over it. (I'm not 100% sure of these behaviors or how best to describe them. But we need to handle the the case of larger-than-viewport items, and an area-based model lets us detect that case and do something intelligent about it, allowing the user to scroll around and see the entire box, rather than being clicked into mandatory positions that only allow partial views.) Finite Snapping =============== When finite snapping is enabled, the "gravitational field" of a snap alignment is two-dimensional: distance to the snap position is calculated for both dimensions at once. In other words, if the snapping radius of influence is r, in infinite snapping the box snaps along the y axis whenever it is within r of its snapped y position, regardless of its x position. But in finite snapping, the box snaps along the y axis whenever it is within r of its snapped position in both dimensions. For example, a small box is snapped to the center of the viewport. It only snaps whenever it is < r distance in any direction from its snap position in both dimensions. In other words, it snaps whenever sqrt(dx^2 + dy^2) <= r for dx, dy as distance to the snapped position in the x and y dimensions respectively. As another example, a small box is snapped to the edges of the viewport. It only snaps whenever matching edges are within r of the respective viewport edges, so e.g. whenever its top edge approaches the top of the viewport, or its left edge approaches the left of the viewport; but there is no snapping effect if those edges are > r outside the viewport. (This feature can be safely deferred to a future level, if necessary.) Combined Models =============== All features in this proposal can be combined within the same scroller: both finite and infinite snapping can co-exist, and both self and group snapping can be combined. Shorthands and Longhands ======================== There would be longhands, e.g. scroll-snap-block and scroll-snap-inline. Exact set TBD, based on need for independent cascading. There would also be a scroll-snap shorthand scroll-snap: <scroll-snap-area> || <scroll-snap-align> || <scroll-snap-scope> Variants on the proposal ======================== 1. Defining <box> = [ border-box | margin-box ] || column-box to have multicols also provide snap lines between columns? 2. Shifting scroll-snap-type to the descendants? (Initial value = proximity. Regions between a mandatory point and proximity points would be mandatory.) 3. Shift scroll-snap-scope to the container? 4. Rename various stuff? There are obviously a lot of details to be worked out, but I think this is a better direction to go in. ~fantasai (and TJ)
Received on Wednesday, 22 July 2015 19:11:19 UTC