Gecko:CSSScrollSnapping: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
Line 1: Line 1:
== Proposal ==
== Proposal ==


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


<tt>scroll-snap-y: none | proximity | mandatory</tt>
<tt>scroll-snap-type-y: none | proximity | mandatory</tt>


<tt>scroll-snap-x: none | proximity | mandatory</tt>
<tt>scroll-snap-type-x: none | proximity | mandatory</tt>


These properties apply to scrollable elements. Values are defined similar to the Microsoft [http://msdn.microsoft.com/en-us/library/ie/hh772038%28v=vs.85%29.aspx scroll-snap-type] property. The default value is "none".
These properties apply to scrollable elements. Values are defined similar to the Microsoft [http://msdn.microsoft.com/en-us/library/ie/hh772038%28v=vs.85%29.aspx scroll-snap-type] property. The default value is "none".
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-edge: none | [ margin-left || margin-start || margin-top || left || start || top ]</tt>
<tt>scroll-snap-edges: none | margin-box | border-box</tt>


This property defines which (if any) edges of the first CSS box for the element contribute to the allowable snapping positions for the nearest scrollable ancestor.
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.


For an element E with 'scroll-snap-x' not 'none', the set of ''horizontal snapping positions'' is the union of
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.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' contains 'margin-left', the left edge of the first margin-box of D if there is one.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' contains 'left', the left edge of the first border-box of D if there is one.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' contains 'margin-start', the left edge of the first margin-box of D if there is one and E's direction is LTR, or the right edge of the first margin-box of D if there is one and E's direction is RTL.
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' contains 'start', the left edge of the first border-box of D if there is one and E's direction is LTR, or the right edge of the first border-box of D if there is one and E's direction is RTL.


For an element E with 'scroll-snap-y' not 'none', the set of ''vertical snapping positions'' is the union of
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:
* For each descendant D of E for which E is the nearest scrollable ancestor, where D's value of 'scroll-snap-edge' contains 'margin-top', the top edge of the first margin-box of D if there is 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', 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' contains 'top', the to edge of the first border-box of D if there is 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.


Transformation of a box edge up to the coordinate space of the scrollable element takes into account CSS positioning but not CSS transforms (so the orientation of the edge is guaranteed to be preserved).
Vertical offsets are handled analogously.


When scrolling an element with 'scroll-snap-y:mandatory' using selected UA scrolling mechanisms, the vertical offset gravitates to one of the vertical snapping positions at the end of the scrolling gesture. If the set of vertical snapping positions is empty, the vertical offset gravitates to zero.
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).


When scrolling an element with 'scroll-snap-y:proximity' using selected UA scrolling mechanisms, the vertical offset gravitates to one of the vertical snapping positions at the end of the scrolling gesture, if it's "sufficiently close" to a snapping position.
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.


Similarly for 'scroll-snap-x'.
''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.''
 
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.
 
The details of snapping behavior (physics) are up to the UA.

Revision as of 10:44, 13 December 2013

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

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.

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 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:

  • 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 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 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. 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.