ScrollView snapToOffsets

Summary:
* Added snapToOffsets prop to ScrollView. Allows snapping at arbitrary points.

* Fixed pagingEnabled not being overridden by snapToInterval on iOS.

* Fixed Android *requiring* pagingEnabled to be defined alongside snapToInterval.
* Added support for decelerationRate on Android.

* Fixed snapping implementation. It was not calculating end position correctly at all (velocity is not a linear offset).
  * Resolves https://github.com/facebook/react-native/issues/20155
* Added support for new content being added during scroll (mirrors existing functionality in vertical ScrollView).

* Added support for snapToInterval.
  * Resolves https://github.com/facebook/react-native/issues/19552

Reviewed By: yungsters

Differential Revision: D9405703

fbshipit-source-id: b3c367b8079e6810794b0165dfdbcff4abff2eda
This commit is contained in:
Oleg Lokhvitsky
2018-08-30 12:59:33 -07:00
committed by Facebook Github Bot
parent 087e2a89fc
commit fd744dd56c
9 changed files with 634 additions and 92 deletions

View File

@@ -125,19 +125,6 @@ type IOSProps = $ReadOnly<{|
* @platform ios
*/
centerContent?: ?boolean,
/**
* A floating-point number that determines how quickly the scroll view
* decelerates after the user lifts their finger. You may also use string
* shortcuts `"normal"` and `"fast"` which match the underlying iOS settings
* for `UIScrollViewDecelerationRateNormal` and
* `UIScrollViewDecelerationRateFast` respectively.
*
* - `'normal'`: 0.998 (the default)
* - `'fast'`: 0.99
*
* @platform ios
*/
decelerationRate?: ?('fast' | 'normal' | number),
/**
* The style of the scroll indicators.
*
@@ -353,6 +340,17 @@ export type Props = $ReadOnly<{|
* ```
*/
contentContainerStyle?: ?ViewStyleProp,
/**
* A floating-point number that determines how quickly the scroll view
* decelerates after the user lifts their finger. You may also use string
* shortcuts `"normal"` and `"fast"` which match the underlying iOS settings
* for `UIScrollViewDecelerationRateNormal` and
* `UIScrollViewDecelerationRateFast` respectively.
*
* - `'normal'`: 0.998 on iOS, 0.985 on Android (the default)
* - `'fast'`: 0.99 on iOS, 0.9 on Android
*/
decelerationRate?: ?('fast' | 'normal' | number),
/**
* When true, the scroll view's children are arranged horizontally in a row
* instead of vertically in a column. The default value is false.
@@ -462,12 +460,20 @@ export type Props = $ReadOnly<{|
* When set, causes the scroll view to stop at multiples of the value of
* `snapToInterval`. This can be used for paginating through children
* that have lengths smaller than the scroll view. Typically used in
* combination with `snapToAlignment` and `decelerationRate="fast"` on ios.
* Overrides less configurable `pagingEnabled` prop.
* combination with `snapToAlignment` and `decelerationRate="fast"`.
*
* Supported for horizontal scrollview on android.
* Overrides less configurable `pagingEnabled` prop.
*/
snapToInterval?: ?number,
/**
* When set, causes the scroll view to stop at the defined offsets.
* This can be used for paginating through variously sized children
* that have lengths smaller than the scroll view. Typically used in
* combination with `decelerationRate="fast"`.
*
* Overrides less configurable `pagingEnabled` and `snapToInterval` props.
*/
snapToOffsets?: ?$ReadOnlyArray<number>,
/**
* Experimental: When true, offscreen child views (whose `overflow` value is
* `hidden`) are removed from their native backing superview when offscreen.
@@ -772,10 +778,6 @@ const ScrollView = createReactClass({
} else {
ScrollViewClass = RCTScrollView;
ScrollContentContainerViewClass = RCTScrollContentView;
warning(
this.props.snapToInterval == null || !this.props.pagingEnabled,
'snapToInterval is currently ignored when pagingEnabled is true.',
);
}
invariant(
@@ -919,6 +921,19 @@ const ScrollView = createReactClass({
? true
: false,
DEPRECATED_sendUpdatedChildFrames,
// pagingEnabled is overridden by snapToInterval / snapToOffsets
pagingEnabled: Platform.select({
// on iOS, pagingEnabled must be set to false to have snapToInterval / snapToOffsets work
ios:
this.props.pagingEnabled &&
this.props.snapToInterval == null &&
this.props.snapToOffsets == null,
// on Android, pagingEnabled must be set to true to have snapToInterval / snapToOffsets work
android:
this.props.pagingEnabled ||
this.props.snapToInterval != null ||
this.props.snapToOffsets != null,
}),
};
const {decelerationRate} = this.props;

View File

@@ -5,15 +5,26 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
function processDecelerationRate(decelerationRate) {
const Platform = require('Platform');
function processDecelerationRate(
decelerationRate: number | 'normal' | 'fast',
): number {
if (decelerationRate === 'normal') {
decelerationRate = 0.998;
return Platform.select({
ios: 0.998,
android: 0.985,
});
} else if (decelerationRate === 'fast') {
decelerationRate = 0.99;
return Platform.select({
ios: 0.99,
android: 0.9,
});
}
return decelerationRate;
}