- 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