Gecko:CSSScrollSnapping: Difference between revisions

Line 11: Line 11:
A scrollable element is one for which 'overflow-x' or 'overflow-y' is 'scroll' or 'auto'.
A scrollable element is one for which 'overflow-x' or 'overflow-y' is 'scroll' or 'auto'.


<tt>scroll-snap-edges: none | margin-box | border-box</tt>
<tt>scroll-snap-edges: none | margin-box | border-box | center</tt>


This property defines which (if any) CSS boxes for the element contribute to the allowable snapping positions for the nearest scrollable ancestor. Which edge of the box is used depends on the direction of the scroll operation. For example, when scrolling down, the bottom edge of the box is aligned with the bottom edge of the scrolling container. More details below.
This property defines which (if any) CSS boxes for the element contribute to the allowable snapping positions for the nearest scrollable ancestor. Which edge of the box is used depends on the direction of the scroll operation. For example, when scrolling down, the bottom edge of the box should align with the bottom edge of the scrolling container. More details below.


Scroll snapping is applied at the end of each complete scroll gesture (e.g. after releasing the finger at the end of a touch panning gesture, after pressing an arrow key) to potentially adjust the final scroll destination. The vocabulary of scroll gestures, and their scrolling behaviors (e.g. animation physics) depends on UA considerations and is not (and should not be) defined here. However, we require that each scroll gesture have a logical start scroll offset and end scroll offset. (These need not correspond to actual rendered scroll offsets. For example if the user presses page-down to start an animated scroll operation, and then presses page-down again before that operation is complete, the second gesture's start offset might be the desired destination of the first gesture rather than the actual scroll position at that time.) Scroll snapping produces a snapped scroll offset to be used as the final scroll destination.
Scroll snapping is applied at the end of each complete scroll gesture (e.g. after releasing the finger at the end of a touch panning gesture, after pressing an arrow key) to potentially adjust the final scroll destination. The vocabulary of scroll gestures, and their scrolling behaviors (e.g. animation physics) depends on UA considerations and is not (and should not be) defined here. However, we require each gesture to have, for each axis, an *optional* direction; i.e. the horizontal direction can be "left", "right" or "unspecified", and the vertical direction can be "up", "down" or "unspecified". We also require each gesture to have an associated "start scroll position", which may or may not correspond to the actual scroll position at the start of the gesture. (E.g., if the user presses page-down twice quickly, the start position for the second page-down gesture may be the expected final destination of the first page-down gesture, even if the scrolling for that gesture hasn't completed yet.)


Scroll snapping for a scrollable element E is defined independently for horizontal and vertical offsets. Horizontally, if 'scroll-snap-x' is 'none' or the start and end offsets are equal, the snapped scroll offset is the end offset. Otherwise we collect the set of candidate boxes C as follows:
At the end of a gesture which scrolls an element E, the UA determines a set of potential snapping opportunities:
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' is 'margin-box', D's first margin-box if it has one.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' is 'margin-box', let B be D's first fragment's margin-box. If B exists, then each edge of B can be snapped to the corresponding edge of E's scroll-port.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' is 'border-box', D's first border-box if it has one.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' is 'border-box', let B be D's first fragment's border-box. If B exists, then each edge of B can be snapped to the corresponding edge of E's scroll-port.
If the start offset is less than the end offset (scrolling to the right), take the right edge of each box in C. The snapped scroll offset is the offset that aligns the right edge of the scrolling container with the right edge of a box in C, and requires the least rightward scrolling. C edges that require leftward or no scrolling are not considered.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' is 'center', let B be D's first fragment's border-box. If B exists, then the center of B can be snapped to the center of E's scroll-port.
If the start offset is greater than the end offset (scrolling to the left), take the left edge of each box in C. The snapped scroll offset is the offset that aligns the left edge of the scrolling container with the left edge of a box in C, and requires the least leftward scrolling. C edges that require rightward or no scrolling are not considered.
* If 'scroll-snap-type-x' is 'proximity', the UA can choose to scroll to the snapped offset or (e.g. if that's too far away from the end offset), it can scroll to the end offset instead. If no C edges can be considered, the snapped offset is the end offset.
* If 'scroll-snap-type-x' is 'mandatory', the UA can choose to scroll to the snapped offset or (e.g. if that's too far away from the end offset), it can reject the scroll gesture entirely. If no C edges can be considered, the snapped offset is the furthest offset in the direction of scrolling.
In any of these cases the UA can animate the scroll operation and adjust the actual scroll position to the destination over some period of time.


Vertical offsets are handled analogously.
These definitions implicitly require transforming a box rectangle up to the coordinate space of the element E. These transformations must take into account CSS positioning but not CSS transforms (so the orientation of boxes is guaranteed to be preserved).


The above algorithm implicitly requires transforming a box edge up to the coordinate space of the scrolling container. These transformations must take into account CSS positioning but not CSS transforms (so the orientation of boxes is guaranteed to be preserved).
Exactly which snapping opportunity (if any) is taken is UA-dependent, but subject to the following constraints:
* If 'scroll-snap-type-x' is 'none', no snapping occurs in the horizontal direction.
* If there are no snapping opportunities, no snapping occurs in the horizontal direction.
* If the gesture's horizontal direction is not "unspecified" and there are no snapping opportunities in the direction of the gesture, searching from the scroll position at the start of the gesture, no snapping occurs in the horizontal direction. (I.e. scroll snapping can't scroll in the opposite direction when a gesture has a definite direction.)
* If the gesture's horizontal direction is "left", snapping opportunities that align the right edges of fragment boxes are ignored.
* If the gesture's horizontal direction is "right", snapping opportunities that align the left edges of fragment boxes are ignored.
* If 'scroll-snap-type-x' is 'mandatory', and none of the above constraints apply, snapping must occur in the horizontal direction.
Similar constraints apply in the vertical direction. When a snapping opportunity is taken, the UA applies additional scrolling to achieve the alignment specified by the snapping opportunity.


UAs should apply scroll snapping to all user scroll gestures (including keyboard, scrollbars, etc). Script-driven scrolling (e.g. setting <tt>scrollLeft</tt> or <tt>scrollTop</tt>) is never affected by scroll snapping. Layout changes that affect the positions of elements with 'scroll-snap-edges', or dynamic changes to values of 'scroll-snap-edges', do not trigger snapping in the absence of a scroll gesture, even if 'mandatory' snapping is requested. However, UAs should ensure that with mandatory snapping, any sequence of scroll gestures followed by a steady state with no style or content changes results in the scroll position at the edge of some scroll-snap-edges element, or the end of the scroll container, depending on the direction of the last scroll gesture that was not rejected.
UAs should apply scroll snapping to all user scroll gestures (including keyboard, scrollbars, etc). Script-driven scrolling (e.g. setting <tt>scrollLeft</tt> or <tt>scrollTop</tt>) is never affected by scroll snapping. Layout changes that affect the positions of elements with 'scroll-snap-edges', or dynamic changes to values of 'scroll-snap-edges', do not trigger snapping in the absence of a scroll gesture, even if 'mandatory' snapping is requested.


''It would be nice to have 'scroll-snap-type:mandatory' ensure that at all times we're scrolled to the snap-edge of some element, even after arbitrary content and style changes. However, it's not clear how that should behave in many cases. For example, without a scroll gesture direction, it's not clear whether you should snap the top of an element to the top of the container or the bottom of an element to the bottom of the container.''
''It would be nice to have 'scroll-snap-type:mandatory' ensure that at all times we're scrolled to the snap-edge of some element, even after arbitrary content and style changes. However, it's not clear how that should behave in many cases. For example, without a scroll gesture direction, it's not clear whether you should snap the top of an element to the top of the container or the bottom of an element to the bottom of the container.''
1,295

edits