Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: swipe scroll flickering #188

Merged
merged 6 commits into from
May 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/PROPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,16 @@ Using this prop will enable/disable pan gesture.
| ---- | -------- | ------- |
| bool | No | `true` |

### `panGestureComponentEnabled`

Define if HeaderComponent/FooterComponent/FloatingComponent should have pan gesture enable (Android specific).

!> Because of a limitation from `react-native-gesture-handler` for Android, when enable it might break touchable inside the view, that's why it's false by default. e.g. you might need to use `TouchableOpacity` from RNGH for Android and `TouchableOpacity` from `react-native` for iOS if set to `true`.

| Type | Required | Default | Platform |
| ---- | -------- | ------- | -------- |
| bool | No | `false` | Android |

### `tapGestureEnabled`

Define if the `TapGestureHandler` wrapping Modalize's core should be enable or not.
Expand All @@ -308,6 +318,16 @@ Using this prop will enable/disable the overlay tap gesture.
| ---- | -------- | ------- |
| bool | No | `true` |

### `closeSnapPointStraightEnabled`

Define if `snapPoint` props should close straight when swiping down or come back to initial position.

?> However, if the velocity value is reached it will close Modalize straight, even when `false`.

| Type | Required | Default |
| ---- | -------- | ------- |
| bool | No | `true` |

## Animations

### `openAnimationConfig`
Expand Down
84 changes: 66 additions & 18 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ const { height: screenHeight } = Dimensions.get('window');
const AnimatedKeyboardAvoidingView = Animated.createAnimatedComponent(KeyboardAvoidingView);
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);
/**
* When scrolling, it happens than beginScrollYValue is not always equal to 0 (top of the ScrollView).
* Since we use this to trigger the swipe down gesture animation, we allow a small threshold to
* not dismiss Modalize when we are using the ScrollView and we don't want to dismiss.
*/
const SCROLL_THRESHOLD = -4;
const USE_NATIVE_DRIVER = true;
const ACTIVATED = 20;
const PAN_DURATION = 150;
Expand Down Expand Up @@ -89,8 +95,10 @@ const ModalizeBase = (
keyboardAvoidingBehavior = 'padding',
keyboardAvoidingOffset,
panGestureEnabled = true,
panGestureComponentEnabled = false,
tapGestureEnabled = true,
closeOnOverlayTap = true,
closeSnapPointStraightEnabled = true,

// Animations
openAnimationConfig = {
Expand All @@ -100,7 +108,7 @@ const ModalizeBase = (
closeAnimationConfig = {
timing: { duration: 240, easing: Easing.ease },
},
dragToss = 0.15,
dragToss = 0.18,
threshold = 120,
velocity = 2800,
panGestureAnimatedValue,
Expand Down Expand Up @@ -148,6 +156,7 @@ const ModalizeBase = (
);
const [beginScrollYValue, setBeginScrollYValue] = React.useState(0);
const [modalPosition, setModalPosition] = React.useState<'top' | 'initial'>('initial');
const [cancelClose, setCancelClose] = React.useState(false);

const cancelTranslateY = React.useRef(new Animated.Value(1)).current; // 1 by default to have the translateY animation running
const componentTranslateY = React.useRef(new Animated.Value(0)).current;
Expand Down Expand Up @@ -402,13 +411,15 @@ const ModalizeBase = (
): void => {
const { timing } = closeAnimationConfig;
const { velocityY, translationY } = nativeEvent;
const enableBouncesValue = isAndroid ? false : beginScrollYValue > 0 || translationY < 0;
const negativeReverseScroll =
modalPosition === 'top' &&
beginScrollYValue >= (snapPoint ? 0 : SCROLL_THRESHOLD) &&
translationY < 0;
const thresholdProps = translationY > threshold && beginScrollYValue === 0;
const closeThreshold = velocity
? (beginScrollYValue <= 20 && velocityY >= velocity) || thresholdProps
: thresholdProps;

setEnableBounces(enableBouncesValue);
let enableBouncesValue = true;

// We make sure to reset the value if we are dragging from the children
if (type !== 'component' && (cancelTranslateY as any)._value === 0) {
Expand All @@ -420,12 +431,21 @@ const ModalizeBase = (
* We cancel the translation animation if the ScrollView is not scrolled to the top
*/
if (nativeEvent.oldState === State.BEGAN) {
if (beginScrollYValue > 0) {
setCancelClose(false);

if (
!closeSnapPointStraightEnabled && snapPoint
? beginScrollYValue > 0
: beginScrollYValue > 0 || negativeReverseScroll
) {
setCancelClose(true);
translateY.setValue(0);
dragY.setValue(0);
cancelTranslateY.setValue(0);
enableBouncesValue = true;
} else {
cancelTranslateY.setValue(1);
enableBouncesValue = false;

if (!tapGestureEnabled) {
setDisableScroll(
Expand All @@ -435,31 +455,59 @@ const ModalizeBase = (
}
}

setEnableBounces(
isAndroid
? false
: alwaysOpen
? beginScrollYValue > 0 || translationY < 0
: enableBouncesValue,
);

if (nativeEvent.oldState === State.ACTIVE) {
const toValue = translationY - beginScrollYValue;
let destSnapPoint = 0;

if (snapPoint || alwaysOpen) {
const endOffsetY = lastSnap + toValue + dragToss * velocityY;

/**
* snapPoint and alwaysOpen use both an array of points to define the first open state and the final state.
*/
snaps.forEach((snap: number) => {
const distFromSnap = Math.abs(snap - endOffsetY);

if (distFromSnap < Math.abs(destSnapPoint - endOffsetY)) {
destSnapPoint = snap;
willCloseModalize = false;

if (alwaysOpen) {
destSnapPoint = (modalHeightValue || 0) - alwaysOpen;
const diffPoint = Math.abs(destSnapPoint - endOffsetY);

// For snapPoint
if (distFromSnap < diffPoint && !alwaysOpen) {
if (closeSnapPointStraightEnabled) {
if (modalPosition === 'initial' && negativeReverseScroll) {
destSnapPoint = snap;
willCloseModalize = false;
}

if (snap === endHeight) {
destSnapPoint = snap;
willCloseModalize = true;
handleClose();
}
} else {
destSnapPoint = snap;
willCloseModalize = false;

if (snap === endHeight) {
willCloseModalize = true;
handleClose();
}
}
}

if (snap === endHeight && !alwaysOpen) {
willCloseModalize = true;
handleClose();
}
// For alwaysOpen props
if (distFromSnap < diffPoint && alwaysOpen && beginScrollYValue <= 0) {
destSnapPoint = (modalHeightValue || 0) - alwaysOpen;
willCloseModalize = false;
}
});
} else if (closeThreshold && !alwaysOpen) {
} else if (closeThreshold && !alwaysOpen && !cancelClose) {
willCloseModalize = true;
handleClose();
}
Expand Down Expand Up @@ -604,7 +652,7 @@ const ModalizeBase = (
* Until a better solution lands in RNGH, I will disable the PanGestureHandler for Android only,
* so inner touchable/gestures are working from the custom components you can pass in.
*/
if (isAndroid) {
if (isAndroid && !panGestureComponentEnabled) {
return tag;
}

Expand Down
12 changes: 12 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ export interface IProps<ListItem = any> {
*/
panGestureEnabled?: boolean;

/**
* Define if HeaderComponent/FooterComponent/FloatingComponent should have pan gesture enable (Android specific). When enable it might break touchable inside the view.
* @default false
*/
panGestureComponentEnabled?: boolean;

/**
* Define if the `TapGestureHandler` wrapping Modalize's core should be enable or not.
* @default true
Expand All @@ -173,6 +179,12 @@ export interface IProps<ListItem = any> {
*/
closeOnOverlayTap?: boolean;

/**
* Define if `snapPoint` props should close straight when swiping down or come back to initial position.
* @default true
*/
closeSnapPointStraightEnabled?: boolean;

/**
* Object to change the open animations.
* @default
Expand Down