Skip to content

Commit 51ec0e7

Browse files
committed
[css-scroll-snap] Add guidance for selecting snap points.
1 parent a689c87 commit 51ec0e7

1 file changed

Lines changed: 162 additions & 64 deletions

File tree

css-scroll-snap/Overview.bs

Lines changed: 162 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,6 @@ Scroll Snapping Model {#snap-model}
225225
as coordinates of the <a>scrollable area</a>
226226
with the 'scroll-snap-points-x' and 'scroll-snap-points-y' properties.
227227

228-
Issue: Discuss inertial vs. semantic scrolling concepts (or drop from spec)
229-
230-
Issue: Discuss 1D vs 2D snapping behaviors... and scoping. (This is all related?)
231-
232228
Scroll Snapping Container {#snap-container}
233229
=========================
234230

@@ -499,67 +495,169 @@ Scroll Snapping Alignment: the 'scroll-snap-align' property {#scroll-snap-align}
499495
-->
500496

501497

502-
Scope of Snapping Influence {#scope}
503-
===========================
504-
505-
Issue: Current spec doesn't define how to select which snap-point to snap to.
506-
See <a href="https://lists.w3.org/Archives/Public/www-style/2015Jul/0325.html">https://lists.w3.org/Archives/Public/www-style/2015Jul/0325.html</a>
507-
for a proposal to ignore snap positions far outside the viewport.
508-
509-
Issue: UAs should be encouraged to ignore snap positions that require scrolling in two dimensions
510-
when a one-dimensional scroll is triggered.
511-
512-
Issue: Define that <a>snap position</a> selection is based on the final scroll position that the scroll physics would land the scroller in after a fling.
513-
514-
<!--
515-
Scoping Snaplines to Visible Objects: the 'scroll-snap-scope' property {#scroll-snap-scope}
516-
---------------------------
517-
518-
<pre class="propdef">
519-
Name: scroll-snap-scope
520-
Value: infinite | finite
521-
Initial: infinite
522-
Applies to: all elements
523-
Inherited: no
524-
Computed value: as specified
525-
Animatable: no
526-
Media: interactive
527-
</pre>
528-
529-
When ''finite'' snapping is enabled,
530-
the "gravitational field" of a snap alignment is two-dimensional:
531-
distance to the snap position is calculated for both dimensions at once.
532-
533-
In other words, if the snapping radius of influence is <var>r</var>,
534-
in infinite snapping the box snaps along the y axis
535-
whenever it is within <var>r</var> of its snapped y position,
536-
regardless of its x position.
537-
But in finite snapping,
538-
the box snaps along the y axis
539-
whenever it is within <var>r</var> of its snapped position
540-
in both dimensions.
541-
542-
<div class="example">
543-
For example, a small box is snapped to the center of the viewport.
544-
It only snaps whenever it is < <var>r</var> distance in any direction
545-
from its snap position in both dimensions.
546-
In other words, it snaps whenever sqrt(<var>d<sub>x</sub></var><sup>2</sup> + <var>d<sub>y</sub></var><sup>2</sup>) &le; <var>r</var>
547-
for <var>d<sub>x</sub></var>,<var>d<sub>y</sub></var> as distance to the snapped position in the x and y dimensions, respectively.
548-
</div>
549-
550-
<div class="example">
551-
As another example, a small box is snapped to the edges of the viewport.
552-
It only snaps whenever matching edges are within <var>r</var> of the respective viewport edges,
553-
so e.g. whenever its top edge approaches the top of the viewport,
554-
or its left edge approaches the left of the viewport;
555-
but there is no snapping effect if those edges are > <var>r</var> outside the viewport.
556-
</div>
498+
Snapping Mechanics {#snap}
499+
==========================
500+
501+
The precise model algorithm to select a <a>snap position</a> to snap to
502+
is intentionally left mostly undefined,
503+
so that user agents can take into account sophisticated models of user intention and interaction
504+
and adjust how they respond over time,
505+
to best serve the user.
506+
507+
This section defines some useful concepts to aid in discussing scroll-snapping mechanics,
508+
and provides some guidelines for what an effective scroll-snapping strategy might look like.
509+
User agents are encouraged to adapt this guidance
510+
and apply their own best judgement
511+
when defining their own snapping behavior.
512+
It also provides a small number of behavior requirements,
513+
to ensure a minimum reasonable behavior that authors can depend on
514+
when designing their interfaces with scroll-snapping in mind.
515+
516+
Types of Scrolling Gestures {#scroll-types}
517+
-------------------------------------------
518+
519+
There are at least three distinct form of scroll gestures that a user might perform on a page,
520+
which can reasonably trigger different snapping behaviors:
521+
522+
: <dfn export local-lt="explicit" lt="explicit scroll">explicit scrolling</dfn>
523+
:: A scroll is <a>explicit</a> if the user is explicitly scrolling to a well-defined and obvious end-point.
524+
This includes gestures such as:
525+
526+
* a touch-based pan,
527+
released without momentum
528+
* manipulating the scrollbar "thumb" explicitly
529+
* programmatically scrolling via APIs such as {{Window/scrollTo()}}.
530+
531+
: <dfn export local-lt="inertial" lt="inertial scroll">inertial scrolling</dfn>
532+
:: A scroll is <a>inertial</a> if it is a gesture where the user "flings" the scroll position,
533+
indicating a direction and a momentum for the scroll,
534+
but no well-defined and intentional end-point.
535+
User agents tend to implement <a>inertial</a> scrolls
536+
by simulating a "friction" force that gradually reduces the scroll's momentum,
537+
or by otherwise gradually reducing the speed in a way feels "natural"
538+
and respects the user's intention.
539+
540+
The scroll position that an <a>inertial</a> scroll would naturally land on
541+
without further intervention is the <dfn noexport>natural end-point</dfn>.
542+
543+
: <dfn export local-lt="semantic" lt="semantic scroll">semantic scrolling</dfn>
544+
:: A scroll is <a>semantic</a> if it expresses a preferred direction to scroll in,
545+
but not a specific amount of scrolling,
546+
or a specific "momentum" to a fling.
547+
This is most commonly from pressing an arrow key;
548+
for example, pressing the Down key indicates that you want to scroll down some amount.
549+
550+
Additionally, because page layouts usually align things vertically and/or horizontally,
551+
UAs sometimes <dfn export>axis-lock</dfn> a scroll when the gesture triggering it
552+
is sufficiently vertical or horizontal.
553+
An <a>axis-locked</a> scroll is usually bound to only scroll along that axis;
554+
this prevents,
555+
for example,
556+
a <em>nearly</em> horizontal fling gesture from gradually drifting up or down as well,
557+
because it is very difficult to fling in a precisely horizontal line.
558+
559+
Types of Snap Points {#snap-types}
560+
----------------------------------
561+
562+
There are two distinct forms of <dfn export lt="scroll snap point" local-lt="snap point">snap points</dfn> that a <a>scroll container</a> might contain:
563+
564+
: <dfn export local-lt="1d" lt="1d snap point">1d snap point</dfn>
565+
:: A <a>1d snap point</a> indicates a desired <a>snap position</a>
566+
in one axis of the <a>scroll container</a> only,
567+
with no preference for what the other axis's position should be.
568+
569+
This is the “default” type of snap point
570+
that most elements will want to use,
571+
and so the ''scroll-snap-align'' property intentionally makes it the simplest to specify.
572+
573+
Note: An element can declare up to two <a>1d snap points</a>,
574+
one in each axis.
575+
These represent two independent <a>snap position</a> preferences.
576+
If one of the element's snap points is chosen in one axis,
577+
this has no bearing on the other--
578+
it might be chosen,
579+
or another element's snap-point might be chosen for that axis,
580+
or that axis might not snap at all.
581+
582+
: <dfn export local-lt="2d" lt="2d snap point">2d snap point</dfn>
583+
:: A <a>2d snap point</a> indicates a desired <a>snap position</a>
584+
in both axises at once,
585+
aligning a particular point in the element
586+
with a corresponding point in the <a>scroll container</a>.
587+
588+
This type of snap point is intended for truly "two-dimensional" layouts,
589+
such as cities on a map
590+
(using ''proximity'' 2d snap points to snap a city to the center of the display when it gets close),
591+
or when the aesthetics of the layout
592+
dictate that an element truly should be laid out in a particular 2d position,
593+
such as an image gallery
594+
(using ''mandatory'' 2d snap points to force each image to be displayed centered on the screen).
595+
596+
Mixing <a>1d</a> and <a>2d</a> snap points within a single <a>scroll container</a> is discouraged,
597+
as the behavior can be hard to predict.
598+
Nevertheless, this specification provides guidance on how to deal with mixed <a>snap points</a>,
599+
as the design of the properties does not prevent such a mixture.
600+
601+
Choosing Snap Points {#choosing}
602+
--------------------------------
603+
604+
An element can have <a>snap points</a> scattered throughout its <a>scrollable area</a>.
605+
A naive algorithm for selecting a <a>snap point</a>
606+
can produce behavior that is unintuitive for users,
607+
so care is required when designing a selection algorithm.
608+
Here are a few pointers that can aid in the selection process:
609+
610+
* <a>Snap points</a> should be chosen to minimize the distance between the end-point
611+
(or the <a>natural end-point</a>)
612+
and the final snapped scroll position,
613+
subject to the additional constraints listed in this section.
614+
615+
* <a>2d snap points</a> are all-or-nothing;
616+
if a <a>2d snap point</a> is chosen to align to,
617+
the <a>scroll container</a> must set its scroll position
618+
according to the snap point's preferred <a>snap position</a> in <em>both</em> axises;
619+
the <a>scroll container</a> <em>must not</em> "partially align" to a <a>2d snap point</a>,
620+
taking its <a>snap position</a> in one axis
621+
and aligning the other axis according to something else.
622+
623+
* If a scroll is <a>axis-locked</a>,
624+
any <a>1d</a> snap points in the other axis should be ignored.
625+
<a>2d</a> snap points should be penalized in the selection process
626+
according to the amount of other-axis scrolling they would cause.
627+
628+
* <a>Snap points</a> should be ignored if their elements are far outside of the "corridor"
629+
that the <a>scroll snap area</a> defines as it moves through the <a>scrollable area</a>
630+
during an <a>inertial scroll</a>,
631+
or a hypothetical "corridor" in the direction of a <a>semantic scroll</a>,
632+
or the <a>scroll snap area</a> after an <a>explicit scroll</a>.
633+
(This is to prevent a far-offscreen element
634+
from having difficult-to-understand effects
635+
on the scroll position.)
636+
637+
* User agents <em>must</em> ensure that a user can "escape" a snap point,
638+
regardless of the scroll method.
639+
For example, if the snap type is ''mandatory''
640+
and the next snap point is more than two screen-widths away,
641+
a naive "always snap to nearest" selection algorithm would "trap" the user
642+
if they were panning with a touch gesture;
643+
a sufficiently large distance would even trap fling scrolling!
644+
Instead, a smarter algorithm that only "returned" to the starting snap point
645+
if the end-point was a fairly small distance from it,
646+
and otherwise ignored the starting snap point,
647+
would give better behavior.
648+
649+
(This implies that a <a>semantic scroll</a> must always ignore the starting <a>snap point</a>.)
650+
651+
* A user agent <em>may</em> want to ignore ''mandatory'' snap points
652+
that are sufficiently far away from the scroll's end point,
653+
to ensure that users are able to see everything on the page
654+
even if the page author did not use snap points well,
655+
even if this means that a <a>scroll container</a> is not aligned to a snap point sometimes.
656+
657+
(This is already taken care of for overly-large elements,
658+
which automatically add additional snap points to themselves
659+
so a user can pan to see the entire element.)
557660

558-
Issue: This feature can be safely deferred to a future level, if necessary.
559-
Alternately it can be dropped and ''finite'' snapping can be the default.
560-
(We can't think of a use case for the infinite snapping model,
561-
except perhaps UA performance.)
562-
-->
563661

564662
Group-based Snapping {#group}
565663
========================

0 commit comments

Comments
 (0)