disable momentum scrolling for horizontal ScrollView (#24045)
Summary: Would like feedback from the community as this may not be the best solution for all I would like to restrict (or paginate) the fling of a horizontal ScrollView when `snapToInterval` is set. This is not currently possible with `pagingEnabled`, since the pagination works only when items are the entire width of the ScrollView. This implementation simply restricts the predicted `targetOffset` found from the `x` velocity and replaces it with the offset when the pan gesture ended. To get pagination working, I may paginate based on the interval by calculating the offset delta from the beginning of the gesture to current offset and restricting the scrolling behavior to the `snapToInterval`. If this is preferred, I can update this PR or make a new one, but wanted to start a discussion since it seems like there are many in the community that would like this feature #21302 . [General] [Added] - add prop `disableIntervalMomentum` to disable the predictive scrolling behavior of horizontal ScrollViews Pull Request resolved: https://github.com/facebook/react-native/pull/24045 Differential Revision: D14939754 Pulled By: sahrens fbshipit-source-id: 26be19c47dfb8eed4d7e6035df53a77451e23081
This commit is contained in:
Родитель
e28b6f314a
Коммит
faaa92bb04
|
@ -368,6 +368,13 @@ export type Props = $ReadOnly<{|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
contentContainerStyle?: ?ViewStyleProp,
|
contentContainerStyle?: ?ViewStyleProp,
|
||||||
|
/**
|
||||||
|
* When true, the scroll view stops on the next index (in relation to scroll
|
||||||
|
* position at release) regardless of how fast the gesture is. This can be
|
||||||
|
* used for horizontal pagination when the page is less than the width of
|
||||||
|
* the ScrollView. The default value is false.
|
||||||
|
*/
|
||||||
|
disableIntervalMomentum?: ?boolean,
|
||||||
/**
|
/**
|
||||||
* A floating-point number that determines how quickly the scroll view
|
* A floating-point number that determines how quickly the scroll view
|
||||||
* decelerates after the user lifts their finger. You may also use string
|
* decelerates after the user lifts their finger. You may also use string
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
@property (nonatomic, copy) NSDictionary *maintainVisibleContentPosition;
|
@property (nonatomic, copy) NSDictionary *maintainVisibleContentPosition;
|
||||||
@property (nonatomic, assign) BOOL scrollToOverflowEnabled;
|
@property (nonatomic, assign) BOOL scrollToOverflowEnabled;
|
||||||
@property (nonatomic, assign) int snapToInterval;
|
@property (nonatomic, assign) int snapToInterval;
|
||||||
|
@property (nonatomic, assign) BOOL disableIntervalMomentum;
|
||||||
@property (nonatomic, copy) NSArray<NSNumber *> *snapToOffsets;
|
@property (nonatomic, copy) NSArray<NSNumber *> *snapToOffsets;
|
||||||
@property (nonatomic, assign) BOOL snapToStart;
|
@property (nonatomic, assign) BOOL snapToStart;
|
||||||
@property (nonatomic, assign) BOOL snapToEnd;
|
@property (nonatomic, assign) BOOL snapToEnd;
|
||||||
|
|
|
@ -855,7 +855,11 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
||||||
|
|
||||||
// What is the current offset?
|
// What is the current offset?
|
||||||
CGFloat velocityAlongAxis = isHorizontal ? velocity.x : velocity.y;
|
CGFloat velocityAlongAxis = isHorizontal ? velocity.x : velocity.y;
|
||||||
CGFloat targetContentOffsetAlongAxis = isHorizontal ? targetContentOffset->x : targetContentOffset->y;
|
CGFloat targetContentOffsetAlongAxis = targetContentOffset->y;
|
||||||
|
if (isHorizontal) {
|
||||||
|
// Use current scroll offset to determine the next index to snap to when momentum disabled
|
||||||
|
targetContentOffsetAlongAxis = self.disableIntervalMomentum ? scrollView.contentOffset.x : targetContentOffset->x;
|
||||||
|
}
|
||||||
|
|
||||||
// Offset based on desired alignment
|
// Offset based on desired alignment
|
||||||
CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height;
|
CGFloat frameLength = isHorizontal ? self.frame.size.width : self.frame.size.height;
|
||||||
|
@ -868,6 +872,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidScrollToTop, onScrollToTop)
|
||||||
|
|
||||||
// Pick snap point based on direction and proximity
|
// Pick snap point based on direction and proximity
|
||||||
CGFloat fractionalIndex = (targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF;
|
CGFloat fractionalIndex = (targetContentOffsetAlongAxis + alignmentOffset) / snapToIntervalF;
|
||||||
|
|
||||||
NSInteger snapIndex =
|
NSInteger snapIndex =
|
||||||
velocityAlongAxis > 0.0 ?
|
velocityAlongAxis > 0.0 ?
|
||||||
ceil(fractionalIndex) :
|
ceil(fractionalIndex) :
|
||||||
|
|
|
@ -85,6 +85,7 @@ RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL)
|
RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
|
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(disableIntervalMomentum, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(snapToOffsets, NSArray<NSNumber *>)
|
RCT_EXPORT_VIEW_PROPERTY(snapToOffsets, NSArray<NSNumber *>)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(snapToStart, BOOL)
|
RCT_EXPORT_VIEW_PROPERTY(snapToStart, BOOL)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(snapToEnd, BOOL)
|
RCT_EXPORT_VIEW_PROPERTY(snapToEnd, BOOL)
|
||||||
|
|
|
@ -65,6 +65,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||||
private @Nullable String mScrollPerfTag;
|
private @Nullable String mScrollPerfTag;
|
||||||
private @Nullable Drawable mEndBackground;
|
private @Nullable Drawable mEndBackground;
|
||||||
private int mEndFillColor = Color.TRANSPARENT;
|
private int mEndFillColor = Color.TRANSPARENT;
|
||||||
|
private boolean mDisableIntervalMomentum = false;
|
||||||
private int mSnapInterval = 0;
|
private int mSnapInterval = 0;
|
||||||
private float mDecelerationRate = 0.985f;
|
private float mDecelerationRate = 0.985f;
|
||||||
private @Nullable List<Integer> mSnapOffsets;
|
private @Nullable List<Integer> mSnapOffsets;
|
||||||
|
@ -141,6 +142,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||||
return mRemoveClippedSubviews;
|
return mRemoveClippedSubviews;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDisableIntervalMomentum(boolean disableIntervalMomentum) {
|
||||||
|
mDisableIntervalMomentum = disableIntervalMomentum;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSendMomentumEvents(boolean sendMomentumEvents) {
|
public void setSendMomentumEvents(boolean sendMomentumEvents) {
|
||||||
mSendMomentumEvents = sendMomentumEvents;
|
mSendMomentumEvents = sendMomentumEvents;
|
||||||
}
|
}
|
||||||
|
@ -579,6 +584,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView implements
|
||||||
|
|
||||||
int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth());
|
int maximumOffset = Math.max(0, computeHorizontalScrollRange() - getWidth());
|
||||||
int targetOffset = predictFinalScrollPosition(velocityX);
|
int targetOffset = predictFinalScrollPosition(velocityX);
|
||||||
|
if (mDisableIntervalMomentum) {
|
||||||
|
targetOffset = getScrollX();
|
||||||
|
}
|
||||||
|
|
||||||
int smallerOffset = 0;
|
int smallerOffset = 0;
|
||||||
int largerOffset = maximumOffset;
|
int largerOffset = maximumOffset;
|
||||||
int firstOffset = 0;
|
int firstOffset = 0;
|
||||||
|
|
|
@ -80,6 +80,11 @@ public class ReactHorizontalScrollViewManager
|
||||||
view.setDecelerationRate(decelerationRate);
|
view.setDecelerationRate(decelerationRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "disableIntervalMomentum")
|
||||||
|
public void setDisableIntervalMomentum(ReactHorizontalScrollView view, boolean disbaleIntervalMomentum) {
|
||||||
|
view.setDisableIntervalMomentum(disbaleIntervalMomentum);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = "snapToInterval")
|
@ReactProp(name = "snapToInterval")
|
||||||
public void setSnapToInterval(ReactHorizontalScrollView view, float snapToInterval) {
|
public void setSnapToInterval(ReactHorizontalScrollView view, float snapToInterval) {
|
||||||
// snapToInterval needs to be exposed as a float because of the Javascript interface.
|
// snapToInterval needs to be exposed as a float because of the Javascript interface.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче