Skip to content

Commit

Permalink
Merge pull request #444 from dohooo/feat/new_rngh_api
Browse files Browse the repository at this point in the history
Feat/new rngh api
  • Loading branch information
dohooo authored Jul 14, 2023
2 parents 4602ff0 + dbf70ef commit 49e6d9c
Show file tree
Hide file tree
Showing 7 changed files with 1,611 additions and 2,008 deletions.
8 changes: 8 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mode": "pre",
"tag": "alpha",
"initialVersions": {
"react-native-reanimated-carousel": "3.5.1"
},
"changesets": []
}
7 changes: 7 additions & 0 deletions .changeset/proud-zebras-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"react-native-reanimated-carousel": major
---

feat: use new RNGH api

Updates `react-native-gesture-handler` to `>=2.9.0` and replaces usage of `useAnimatedGestureHandler` with the [new gesture handler API](https://docs.swmansion.com/react-native-gesture-handler/docs/#rngh-20) which supports the [new gesture handler web implementation](https://github.com/software-mansion/react-native-gesture-handler/pull/2157).
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,13 @@ Or if you use npm:
npm install react-native-reanimated-carousel
```

Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated(>=2.0.0)`](https://github.com/kmagiera/react-native-reanimated).
Now we need to install [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler) and [`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated).

| | react-native-reanimated | react-native-gesture-handler |
| -------------------------------------- | ----------------------- | ---------------------------- |
| react-native-reanimated-carousel < v3 | <2.7.0 | \* |
| react-native-reanimated-carousel >= v3 | >=2.7.0 | \* |
| react-native-reanimated-carousel v1、v2 | >=2.0 & <2.7.0 | <2.9.0 |
| react-native-reanimated-carousel v3 | >=2.7.0 & < 3.x | <2.9.0 |
| react-native-reanimated-carousel v4 | >=3.x | >=2.9.0 |

## Usage

Expand Down
10 changes: 6 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ yarn add react-native-reanimated-carousel
npm install react-native-reanimated-carousel
```

并且我们需要安装 [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler)[`react-native-reanimated(>=2.0.0)`](https://github.com/kmagiera/react-native-reanimated),安装步骤可参考各自文档。
| | react-native-reanimated | react-native-gesture-handler |
并且我们需要安装 [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler)[`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated),安装步骤可参考各自文档。
| | react-native-reanimated | react-native-gesture-handler |
| -------------------------------------- | ----------------------- | ---------------------------- |
| react-native-reanimated-carousel < v3 | <2.7.0 | \* |
| react-native-reanimated-carousel >= v3 | >=2.7.0 | \* |
| react-native-reanimated-carousel v1、v2 | >=2.0 & <2.7.0 | <2.9.0 |
| react-native-reanimated-carousel v3 | >=2.7.0 & < 3.x | <2.9.0 |
| react-native-reanimated-carousel v4 | >=3.x | >=2.9.0 |


## 使用

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@
"watch": "^1.0.2"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-native": ">=0.6.0",
"react-native-gesture-handler": ">=2.0.0",
"react": ">=18.0.0",
"react-native": ">=0.70.3",
"react-native-gesture-handler": ">=2.9.0",
"react-native-reanimated": ">=3.0.0"
},
"jest": {
Expand Down
203 changes: 111 additions & 92 deletions src/ScrollViewGesture.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { PropsWithChildren } from "react";
import React from "react";
import React, { useCallback, useMemo } from "react";
import type { StyleProp, ViewStyle } from "react-native";
import type { PanGestureHandlerGestureEvent } from "react-native-gesture-handler";
import { PanGestureHandler } from "react-native-gesture-handler";
import type { GestureStateChangeEvent, PanGestureHandlerEventPayload } from "react-native-gesture-handler";
import {
Gesture,
GestureDetector,
} from "react-native-gesture-handler";
import Animated, {
cancelAnimation,
measure,
runOnJS,
useAnimatedGestureHandler,
useAnimatedReaction,
useAnimatedRef,
useDerivedValue,
Expand All @@ -20,12 +22,6 @@ import { CTX } from "./store";
import type { WithTimingAnimation } from "./types";
import { dealWithAnimation } from "./utils/dealWithAnimation";

interface GestureContext extends Record<string, unknown> {
validStart: boolean
panOffset: number
max: number
}

interface Props {
size: number
infinite?: boolean
Expand All @@ -44,7 +40,6 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
vertical,
pagingEnabled,
snapEnabled,
panGestureHandlerProps,
loop: infinite,
scrollAnimationDuration,
withAnimation,
Expand All @@ -68,10 +63,14 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {

const maxPage = dataLength;
const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
const max = useSharedValue(0);
const panOffset = useSharedValue(0);
const touching = useSharedValue(false);
const validStart = useSharedValue(false);
const scrollEndTranslation = useSharedValue(0);
const scrollEndVelocity = useSharedValue(0);
const containerRef = useAnimatedRef<Animated.View>();
const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number";

// Get the limit of the scroll.
const getLimit = React.useCallback(() => {
Expand Down Expand Up @@ -123,7 +122,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
let finalTranslation: number = withDecay({ velocity, deceleration: 0.999 });

// If the distance of the swipe exceeds the max scroll distance, keep the view at the current position
if (typeof maxScrollDistancePerSwipe === "number" && Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe) {
if (maxScrollDistancePerSwipeIsSet && Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe) {
finalTranslation = origin;
}
else {
Expand Down Expand Up @@ -180,6 +179,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
scrollEndVelocity.value,
maxScrollDistancePerSwipe,
scrollEndTranslation.value,
maxScrollDistancePerSwipeIsSet,
],
);

Expand Down Expand Up @@ -259,96 +259,115 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
return translation;
}

const panGestureEventHandler = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
GestureContext
>(
{
onStart: (_, ctx) => {
touching.value = true;
ctx.validStart = true;
onScrollBegin && runOnJS(onScrollBegin)();

ctx.max = (maxPage - 1) * size;
if (!infinite && !overscrollEnabled)
ctx.max = getLimit();

ctx.panOffset = translation.value;
},
onActive: (e, ctx) => {
if (ctx.validStart) {
ctx.validStart = false;
cancelAnimation(translation);
}
touching.value = true;
let { translationX, translationY } = e;
const onGestureBegin = useCallback(() => {
"worklet";

const totalTranslation = isHorizontal.value ? translationX : translationY;
touching.value = true;
validStart.value = true;
onScrollBegin && runOnJS(onScrollBegin)();

if (typeof maxScrollDistancePerSwipe === "number" && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
const overSwipe = Math.abs(totalTranslation) - maxScrollDistancePerSwipe;
const dampedTranslation = maxScrollDistancePerSwipe + overSwipe * 0.5;
max.value = (maxPage - 1) * size;
if (!infinite && !overscrollEnabled)
max.value = getLimit();

translationX = isHorizontal.value ? dampedTranslation * Math.sign(translationX) : translationX;
translationY = !isHorizontal.value ? dampedTranslation * Math.sign(translationY) : translationY;
}
panOffset.value = translation.value;
}, [
max,
size,
maxPage,
infinite,
touching,
panOffset,
validStart,
translation,
overscrollEnabled,
getLimit,
onScrollBegin,
]);

const panTranslation = isHorizontal.value ? translationX : translationY;
if (!infinite) {
if ((translation.value > 0 || translation.value < -ctx.max)) {
const boundary = translation.value > 0 ? 0 : -ctx.max;
const fixed = boundary - ctx.panOffset;
const dynamic = panTranslation - fixed;
translation.value = boundary + dynamic * 0.5;
return;
}
}
const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
"worklet";

if (validStart.value) {
validStart.value = false;
cancelAnimation(translation);
}
touching.value = true;
const { translationX, translationY } = e;
const panTranslation = isHorizontal.value
? translationX
: translationY;
if (!infinite) {
if ((translation.value > 0 || translation.value < -max.value)) {
const boundary = translation.value > 0 ? 0 : -max.value;
const fixed = boundary - panOffset.value;
const dynamic = panTranslation - fixed;
translation.value = boundary + dynamic * 0.5;
return;
}
}

const translationValue = panOffset.value + panTranslation;
translation.value = translationValue;
}, [
isHorizontal,
max,
panOffset,
infinite,
overscrollEnabled,
translation,
validStart,
touching,
]);

const translationValue = ctx.panOffset + panTranslation;
const onGestureFinish = useCallback((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
"worklet";

translation.value = translationValue;
},
onEnd: (e, ctx) => {
const { velocityX, velocityY, translationX, translationY } = e;
scrollEndVelocity.value = isHorizontal.value
? velocityX
: velocityY;
scrollEndTranslation.value = isHorizontal.value
? translationX
: translationY;
const { velocityX, velocityY, translationX, translationY } = e;
scrollEndVelocity.value = isHorizontal.value
? velocityX
: velocityY;
scrollEndTranslation.value = isHorizontal.value
? translationX
: translationY;

const totalTranslation = isHorizontal.value ? translationX : translationY;
const totalTranslation = scrollEndVelocity.value + scrollEndTranslation.value;

if (typeof maxScrollDistancePerSwipe === "number" && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
const nextPage = Math.round((ctx.panOffset + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
}
else {
endWithSpring(onScrollEnd);
}
if (maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
const nextPage = Math.round((panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
}
else {
endWithSpring(onScrollEnd);
}

if (!infinite)
touching.value = false;
},
},
[
pagingEnabled,
isHorizontal.value,
infinite,
maxPage,
size,
snapEnabled,
onScrollBegin,
onScrollEnd,
],
);
if (!infinite)
touching.value = false;
}, [
size,
infinite,
touching,
panOffset,
translation,
isHorizontal,
scrollEndVelocity,
scrollEndTranslation,
maxScrollDistancePerSwipeIsSet,
maxScrollDistancePerSwipe,
endWithSpring,
withSpring,
onScrollEnd,
]);

const gesture = useMemo(() => Gesture.Pan().onBegin(onGestureBegin).onUpdate(onGestureUpdate).onEnd(onGestureFinish), [
onGestureBegin,
onGestureUpdate,
onGestureFinish,
]);
const GestureContainer = enabled ? GestureDetector : React.Fragment;

return (
<PanGestureHandler
{...panGestureHandlerProps}
enabled={enabled}
onGestureEvent={panGestureEventHandler}
>
<GestureContainer gesture={gesture}>
<Animated.View
ref={containerRef}
testID={testID}
Expand All @@ -358,7 +377,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
>
{props.children}
</Animated.View>
</PanGestureHandler>
</GestureContainer>
);
};

Expand Down
Loading

0 comments on commit 49e6d9c

Please sign in to comment.