Skip to content

Commit 49e6d9c

Browse files
authored
Merge pull request #444 from dohooo/feat/new_rngh_api
Feat/new rngh api
2 parents 4602ff0 + dbf70ef commit 49e6d9c

File tree

7 files changed

+1611
-2008
lines changed

7 files changed

+1611
-2008
lines changed

.changeset/pre.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mode": "pre",
3+
"tag": "alpha",
4+
"initialVersions": {
5+
"react-native-reanimated-carousel": "3.5.1"
6+
},
7+
"changesets": []
8+
}

.changeset/proud-zebras-jump.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-native-reanimated-carousel": major
3+
---
4+
5+
feat: use new RNGH api
6+
7+
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).

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ Or if you use npm:
7676
npm install react-native-reanimated-carousel
7777
```
7878

79-
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).
79+
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).
8080

8181
| | react-native-reanimated | react-native-gesture-handler |
8282
| -------------------------------------- | ----------------------- | ---------------------------- |
83-
| react-native-reanimated-carousel < v3 | <2.7.0 | \* |
84-
| react-native-reanimated-carousel >= v3 | >=2.7.0 | \* |
83+
| react-native-reanimated-carousel v1、v2 | >=2.0 & <2.7.0 | <2.9.0 |
84+
| react-native-reanimated-carousel v3 | >=2.7.0 & < 3.x | <2.9.0 |
85+
| react-native-reanimated-carousel v4 | >=3.x | >=2.9.0 |
8586

8687
## Usage
8788

README.zh-CN.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ yarn add react-native-reanimated-carousel
7777
npm install react-native-reanimated-carousel
7878
```
7979

80-
并且我们需要安装 [`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),安装步骤可参考各自文档。
81-
| | react-native-reanimated | react-native-gesture-handler |
80+
并且我们需要安装 [`react-native-gesture-handler`](https://github.com/kmagiera/react-native-gesture-handler)[`react-native-reanimated`](https://github.com/kmagiera/react-native-reanimated),安装步骤可参考各自文档。
81+
| | react-native-reanimated | react-native-gesture-handler |
8282
| -------------------------------------- | ----------------------- | ---------------------------- |
83-
| react-native-reanimated-carousel < v3 | <2.7.0 | \* |
84-
| react-native-reanimated-carousel >= v3 | >=2.7.0 | \* |
83+
| react-native-reanimated-carousel v1、v2 | >=2.0 & <2.7.0 | <2.9.0 |
84+
| react-native-reanimated-carousel v3 | >=2.7.0 & < 3.x | <2.9.0 |
85+
| react-native-reanimated-carousel v4 | >=3.x | >=2.9.0 |
86+
8587

8688
## 使用
8789

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@
8585
"watch": "^1.0.2"
8686
},
8787
"peerDependencies": {
88-
"react": ">=16.8.0",
89-
"react-native": ">=0.6.0",
90-
"react-native-gesture-handler": ">=2.0.0",
88+
"react": ">=18.0.0",
89+
"react-native": ">=0.70.3",
90+
"react-native-gesture-handler": ">=2.9.0",
9191
"react-native-reanimated": ">=3.0.0"
9292
},
9393
"jest": {

src/ScrollViewGesture.tsx

Lines changed: 111 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import type { PropsWithChildren } from "react";
2-
import React from "react";
2+
import React, { useCallback, useMemo } from "react";
33
import type { StyleProp, ViewStyle } from "react-native";
4-
import type { PanGestureHandlerGestureEvent } from "react-native-gesture-handler";
5-
import { PanGestureHandler } from "react-native-gesture-handler";
4+
import type { GestureStateChangeEvent, PanGestureHandlerEventPayload } from "react-native-gesture-handler";
5+
import {
6+
Gesture,
7+
GestureDetector,
8+
} from "react-native-gesture-handler";
69
import Animated, {
710
cancelAnimation,
811
measure,
912
runOnJS,
10-
useAnimatedGestureHandler,
1113
useAnimatedReaction,
1214
useAnimatedRef,
1315
useDerivedValue,
@@ -20,12 +22,6 @@ import { CTX } from "./store";
2022
import type { WithTimingAnimation } from "./types";
2123
import { dealWithAnimation } from "./utils/dealWithAnimation";
2224

23-
interface GestureContext extends Record<string, unknown> {
24-
validStart: boolean
25-
panOffset: number
26-
max: number
27-
}
28-
2925
interface Props {
3026
size: number
3127
infinite?: boolean
@@ -44,7 +40,6 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
4440
vertical,
4541
pagingEnabled,
4642
snapEnabled,
47-
panGestureHandlerProps,
4843
loop: infinite,
4944
scrollAnimationDuration,
5045
withAnimation,
@@ -68,10 +63,14 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
6863

6964
const maxPage = dataLength;
7065
const isHorizontal = useDerivedValue(() => !vertical, [vertical]);
66+
const max = useSharedValue(0);
67+
const panOffset = useSharedValue(0);
7168
const touching = useSharedValue(false);
69+
const validStart = useSharedValue(false);
7270
const scrollEndTranslation = useSharedValue(0);
7371
const scrollEndVelocity = useSharedValue(0);
7472
const containerRef = useAnimatedRef<Animated.View>();
73+
const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number";
7574

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

125124
// If the distance of the swipe exceeds the max scroll distance, keep the view at the current position
126-
if (typeof maxScrollDistancePerSwipe === "number" && Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe) {
125+
if (maxScrollDistancePerSwipeIsSet && Math.abs(scrollEndTranslation.value) > maxScrollDistancePerSwipe) {
127126
finalTranslation = origin;
128127
}
129128
else {
@@ -180,6 +179,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
180179
scrollEndVelocity.value,
181180
maxScrollDistancePerSwipe,
182181
scrollEndTranslation.value,
182+
maxScrollDistancePerSwipeIsSet,
183183
],
184184
);
185185

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

262-
const panGestureEventHandler = useAnimatedGestureHandler<
263-
PanGestureHandlerGestureEvent,
264-
GestureContext
265-
>(
266-
{
267-
onStart: (_, ctx) => {
268-
touching.value = true;
269-
ctx.validStart = true;
270-
onScrollBegin && runOnJS(onScrollBegin)();
271-
272-
ctx.max = (maxPage - 1) * size;
273-
if (!infinite && !overscrollEnabled)
274-
ctx.max = getLimit();
275-
276-
ctx.panOffset = translation.value;
277-
},
278-
onActive: (e, ctx) => {
279-
if (ctx.validStart) {
280-
ctx.validStart = false;
281-
cancelAnimation(translation);
282-
}
283-
touching.value = true;
284-
let { translationX, translationY } = e;
262+
const onGestureBegin = useCallback(() => {
263+
"worklet";
285264

286-
const totalTranslation = isHorizontal.value ? translationX : translationY;
265+
touching.value = true;
266+
validStart.value = true;
267+
onScrollBegin && runOnJS(onScrollBegin)();
287268

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

292-
translationX = isHorizontal.value ? dampedTranslation * Math.sign(translationX) : translationX;
293-
translationY = !isHorizontal.value ? dampedTranslation * Math.sign(translationY) : translationY;
294-
}
273+
panOffset.value = translation.value;
274+
}, [
275+
max,
276+
size,
277+
maxPage,
278+
infinite,
279+
touching,
280+
panOffset,
281+
validStart,
282+
translation,
283+
overscrollEnabled,
284+
getLimit,
285+
onScrollBegin,
286+
]);
295287

296-
const panTranslation = isHorizontal.value ? translationX : translationY;
297-
if (!infinite) {
298-
if ((translation.value > 0 || translation.value < -ctx.max)) {
299-
const boundary = translation.value > 0 ? 0 : -ctx.max;
300-
const fixed = boundary - ctx.panOffset;
301-
const dynamic = panTranslation - fixed;
302-
translation.value = boundary + dynamic * 0.5;
303-
return;
304-
}
305-
}
288+
const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
289+
"worklet";
290+
291+
if (validStart.value) {
292+
validStart.value = false;
293+
cancelAnimation(translation);
294+
}
295+
touching.value = true;
296+
const { translationX, translationY } = e;
297+
const panTranslation = isHorizontal.value
298+
? translationX
299+
: translationY;
300+
if (!infinite) {
301+
if ((translation.value > 0 || translation.value < -max.value)) {
302+
const boundary = translation.value > 0 ? 0 : -max.value;
303+
const fixed = boundary - panOffset.value;
304+
const dynamic = panTranslation - fixed;
305+
translation.value = boundary + dynamic * 0.5;
306+
return;
307+
}
308+
}
309+
310+
const translationValue = panOffset.value + panTranslation;
311+
translation.value = translationValue;
312+
}, [
313+
isHorizontal,
314+
max,
315+
panOffset,
316+
infinite,
317+
overscrollEnabled,
318+
translation,
319+
validStart,
320+
touching,
321+
]);
306322

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

309-
translation.value = translationValue;
310-
},
311-
onEnd: (e, ctx) => {
312-
const { velocityX, velocityY, translationX, translationY } = e;
313-
scrollEndVelocity.value = isHorizontal.value
314-
? velocityX
315-
: velocityY;
316-
scrollEndTranslation.value = isHorizontal.value
317-
? translationX
318-
: translationY;
326+
const { velocityX, velocityY, translationX, translationY } = e;
327+
scrollEndVelocity.value = isHorizontal.value
328+
? velocityX
329+
: velocityY;
330+
scrollEndTranslation.value = isHorizontal.value
331+
? translationX
332+
: translationY;
319333

320-
const totalTranslation = isHorizontal.value ? translationX : translationY;
334+
const totalTranslation = scrollEndVelocity.value + scrollEndTranslation.value;
321335

322-
if (typeof maxScrollDistancePerSwipe === "number" && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
323-
const nextPage = Math.round((ctx.panOffset + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
324-
translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
325-
}
326-
else {
327-
endWithSpring(onScrollEnd);
328-
}
336+
if (maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
337+
const nextPage = Math.round((panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
338+
translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
339+
}
340+
else {
341+
endWithSpring(onScrollEnd);
342+
}
329343

330-
if (!infinite)
331-
touching.value = false;
332-
},
333-
},
334-
[
335-
pagingEnabled,
336-
isHorizontal.value,
337-
infinite,
338-
maxPage,
339-
size,
340-
snapEnabled,
341-
onScrollBegin,
342-
onScrollEnd,
343-
],
344-
);
344+
if (!infinite)
345+
touching.value = false;
346+
}, [
347+
size,
348+
infinite,
349+
touching,
350+
panOffset,
351+
translation,
352+
isHorizontal,
353+
scrollEndVelocity,
354+
scrollEndTranslation,
355+
maxScrollDistancePerSwipeIsSet,
356+
maxScrollDistancePerSwipe,
357+
endWithSpring,
358+
withSpring,
359+
onScrollEnd,
360+
]);
361+
362+
const gesture = useMemo(() => Gesture.Pan().onBegin(onGestureBegin).onUpdate(onGestureUpdate).onEnd(onGestureFinish), [
363+
onGestureBegin,
364+
onGestureUpdate,
365+
onGestureFinish,
366+
]);
367+
const GestureContainer = enabled ? GestureDetector : React.Fragment;
345368

346369
return (
347-
<PanGestureHandler
348-
{...panGestureHandlerProps}
349-
enabled={enabled}
350-
onGestureEvent={panGestureEventHandler}
351-
>
370+
<GestureContainer gesture={gesture}>
352371
<Animated.View
353372
ref={containerRef}
354373
testID={testID}
@@ -358,7 +377,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
358377
>
359378
{props.children}
360379
</Animated.View>
361-
</PanGestureHandler>
380+
</GestureContainer>
362381
);
363382
};
364383

0 commit comments

Comments
 (0)