Gecko:CSSScrollSnapping: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
 
(3 intermediate revisions by the same user not shown)
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.


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. At the end of a scroll 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', 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', 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.
* 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.


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:
These definitions implicitly require transforming a box rectangle up to the coordinate space of the element E. When CSS transforms are present, preserve axis-alignment by using the smallest axis-aligned rectangle that contains the transformed box.
* 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 'border-box', D's first border-box if it has one.
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.
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.
The algorithm for computing which snapping opportunity (if any) to take is gesture and UA dependent. However the following constraints must be honored:
* If the scroll gesture has a definite direction (this is UA-dependent, but it includes clicking on scrollbar buttons or pressing up/down keys), the UA must not choose a snapping opportunity that makes the gesture result in scrolling in the opposite direction.
* When scrolling along an axis for which 'scroll-snap-type' is 'mandatory', the UA must choose one of the snapping opportunities for that axis (if there are any valid opportunities, subject to the previous requirement). However this requirement is lifted if one or more of the snapping opportunities' boxes B is larger than the scroll-port.
* When 'scroll-snap-type' is 'none' for some axis, no scroll snapping occurs along that axis.


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).
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.
 
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.
 
''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.''

Latest revision as of 22:15, 4 March 2014

Proposal

scroll-snap-type: none | proximity | mandatory (shorthand setting both x and y)

scroll-snap-type-y: none | proximity | mandatory

scroll-snap-type-x: none | proximity | mandatory

These properties apply to scrollable elements. Values are defined similar to the Microsoft scroll-snap-type property. The default value is "none".

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

scroll-snap-edges: none | margin-box | border-box | center

This property defines which (if any) CSS boxes for the element contribute to the allowable snapping positions for the nearest scrollable ancestor.

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. At the end of a scroll 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', 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', 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.
  • 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.

These definitions implicitly require transforming a box rectangle up to the coordinate space of the element E. When CSS transforms are present, preserve axis-alignment by using the smallest axis-aligned rectangle that contains the transformed box.

The algorithm for computing which snapping opportunity (if any) to take is gesture and UA dependent. However the following constraints must be honored:

  • If the scroll gesture has a definite direction (this is UA-dependent, but it includes clicking on scrollbar buttons or pressing up/down keys), the UA must not choose a snapping opportunity that makes the gesture result in scrolling in the opposite direction.
  • When scrolling along an axis for which 'scroll-snap-type' is 'mandatory', the UA must choose one of the snapping opportunities for that axis (if there are any valid opportunities, subject to the previous requirement). However this requirement is lifted if one or more of the snapping opportunities' boxes B is larger than the scroll-port.
  • When 'scroll-snap-type' is 'none' for some axis, no scroll snapping occurs along that axis.

UAs should apply scroll snapping to all user scroll gestures (including keyboard, scrollbars, etc). Script-driven scrolling (e.g. setting scrollLeft or scrollTop) 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.