react-native-skia-gesture 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +121 -0
- package/lib/commonjs/canvas/canvas.js +64 -0
- package/lib/commonjs/canvas/canvas.js.map +1 -0
- package/lib/commonjs/canvas/context.js +18 -0
- package/lib/commonjs/canvas/context.js.map +1 -0
- package/lib/commonjs/canvas/index.js +17 -0
- package/lib/commonjs/canvas/index.js.map +1 -0
- package/lib/commonjs/hoc/index.js +17 -0
- package/lib/commonjs/hoc/index.js.map +1 -0
- package/lib/commonjs/hoc/with-touchable-handler.js +94 -0
- package/lib/commonjs/hoc/with-touchable-handler.js.map +1 -0
- package/lib/commonjs/hooks/use-gesture-handler.js +35 -0
- package/lib/commonjs/hooks/use-gesture-handler.js.map +1 -0
- package/lib/commonjs/index.js +39 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/utils/get-circle-path.js +17 -0
- package/lib/commonjs/utils/get-circle-path.js.map +1 -0
- package/lib/commonjs/utils/get-rect-path.js +48 -0
- package/lib/commonjs/utils/get-rect-path.js.map +1 -0
- package/lib/commonjs/utils/unwrap-animated-value.js +23 -0
- package/lib/commonjs/utils/unwrap-animated-value.js.map +1 -0
- package/lib/module/canvas/canvas.js +56 -0
- package/lib/module/canvas/canvas.js.map +1 -0
- package/lib/module/canvas/context.js +9 -0
- package/lib/module/canvas/context.js.map +1 -0
- package/lib/module/canvas/index.js +2 -0
- package/lib/module/canvas/index.js.map +1 -0
- package/lib/module/hoc/index.js +2 -0
- package/lib/module/hoc/index.js.map +1 -0
- package/lib/module/hoc/with-touchable-handler.js +88 -0
- package/lib/module/hoc/with-touchable-handler.js.map +1 -0
- package/lib/module/hooks/use-gesture-handler.js +29 -0
- package/lib/module/hooks/use-gesture-handler.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/utils/get-circle-path.js +10 -0
- package/lib/module/utils/get-circle-path.js.map +1 -0
- package/lib/module/utils/get-rect-path.js +41 -0
- package/lib/module/utils/get-rect-path.js.map +1 -0
- package/lib/module/utils/unwrap-animated-value.js +16 -0
- package/lib/module/utils/unwrap-animated-value.js.map +1 -0
- package/lib/typescript/canvas/canvas.d.ts +5 -0
- package/lib/typescript/canvas/canvas.d.ts.map +1 -0
- package/lib/typescript/canvas/context.d.ts +14 -0
- package/lib/typescript/canvas/context.d.ts.map +1 -0
- package/lib/typescript/canvas/index.d.ts +2 -0
- package/lib/typescript/canvas/index.d.ts.map +1 -0
- package/lib/typescript/hoc/index.d.ts +2 -0
- package/lib/typescript/hoc/index.d.ts.map +1 -0
- package/lib/typescript/hoc/with-touchable-handler.d.ts +15 -0
- package/lib/typescript/hoc/with-touchable-handler.d.ts.map +1 -0
- package/lib/typescript/hooks/use-gesture-handler.d.ts +14 -0
- package/lib/typescript/hooks/use-gesture-handler.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/utils/get-circle-path.d.ts +8 -0
- package/lib/typescript/utils/get-circle-path.d.ts.map +1 -0
- package/lib/typescript/utils/get-rect-path.d.ts +16 -0
- package/lib/typescript/utils/get-rect-path.d.ts.map +1 -0
- package/lib/typescript/utils/unwrap-animated-value.d.ts +5 -0
- package/lib/typescript/utils/unwrap-animated-value.d.ts.map +1 -0
- package/package.json +160 -0
- package/src/canvas/canvas.tsx +66 -0
- package/src/canvas/context.tsx +27 -0
- package/src/canvas/index.ts +1 -0
- package/src/hoc/index.ts +1 -0
- package/src/hoc/with-touchable-handler.tsx +127 -0
- package/src/hooks/use-gesture-handler.ts +59 -0
- package/src/index.ts +3 -0
- package/src/utils/get-circle-path.ts +7 -0
- package/src/utils/get-rect-path.ts +41 -0
- package/src/utils/unwrap-animated-value.ts +18 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
import {
|
2
|
+
ExtendedTouchInfo,
|
3
|
+
SkiaProps,
|
4
|
+
SkiaValue,
|
5
|
+
SkPath,
|
6
|
+
TouchInfo,
|
7
|
+
useValue,
|
8
|
+
Vector,
|
9
|
+
} from '@shopify/react-native-skia';
|
10
|
+
import { useCallback, useEffect, useId } from 'react';
|
11
|
+
import { getCirclePath } from '../utils/get-circle-path';
|
12
|
+
import { getRectPath, getRoundedRectPath } from '../utils/get-rect-path';
|
13
|
+
import {
|
14
|
+
unwrapAnimatedValue,
|
15
|
+
unwrapAnimatedValueObject,
|
16
|
+
} from '../utils/unwrap-animated-value';
|
17
|
+
import { useTouchHandlerContext } from '../canvas/context';
|
18
|
+
|
19
|
+
export type TranslationInfo = {
|
20
|
+
translationX: number;
|
21
|
+
translationY: number;
|
22
|
+
};
|
23
|
+
|
24
|
+
export type TouchableHandlerProps = {
|
25
|
+
onStart: (touchInfo: TouchInfo) => void;
|
26
|
+
onActive: (touchInfo: ExtendedTouchInfo & TranslationInfo) => void;
|
27
|
+
onEnd: (touchInfo: ExtendedTouchInfo & TranslationInfo) => void;
|
28
|
+
touchablePath: SkPath | SkiaValue<SkPath>;
|
29
|
+
};
|
30
|
+
|
31
|
+
type WithTouchableHandlerProps<T> = SkiaProps<T> &
|
32
|
+
Partial<TouchableHandlerProps>;
|
33
|
+
|
34
|
+
const getSkiaPath = (key: string, props: any) => {
|
35
|
+
const unwrappedProps = unwrapAnimatedValueObject(props) as any;
|
36
|
+
|
37
|
+
switch (key) {
|
38
|
+
case 'Circle':
|
39
|
+
return getCirclePath(unwrappedProps);
|
40
|
+
case 'Rect':
|
41
|
+
return getRectPath(unwrappedProps);
|
42
|
+
case 'RoundedRect':
|
43
|
+
return getRoundedRectPath(unwrappedProps);
|
44
|
+
default:
|
45
|
+
return null;
|
46
|
+
}
|
47
|
+
};
|
48
|
+
|
49
|
+
const withTouchableHandler = <T,>(
|
50
|
+
Component: (props: WithTouchableHandlerProps<T>) => JSX.Element
|
51
|
+
) => {
|
52
|
+
return ({
|
53
|
+
onStart: onStartProp,
|
54
|
+
onActive: onActiveProp,
|
55
|
+
onEnd: onEndProp,
|
56
|
+
touchablePath,
|
57
|
+
...props
|
58
|
+
}: WithTouchableHandlerProps<T>) => {
|
59
|
+
const id = useId();
|
60
|
+
const ref = useTouchHandlerContext();
|
61
|
+
|
62
|
+
const startingPoint = useValue<Vector | null>(null);
|
63
|
+
|
64
|
+
const onStart: TouchableHandlerProps['onStart'] = useCallback(
|
65
|
+
(event) => {
|
66
|
+
startingPoint.current = { x: event.x, y: event.y };
|
67
|
+
return onStartProp?.(event);
|
68
|
+
},
|
69
|
+
[onStartProp, startingPoint]
|
70
|
+
);
|
71
|
+
const onActive: TouchableHandlerProps['onActive'] = useCallback(
|
72
|
+
(event) => {
|
73
|
+
const translationX = event.x - (startingPoint.current?.x ?? 0);
|
74
|
+
const translationY = event.y - (startingPoint.current?.y ?? 0);
|
75
|
+
return onActiveProp?.({
|
76
|
+
...event,
|
77
|
+
translationX,
|
78
|
+
translationY,
|
79
|
+
});
|
80
|
+
},
|
81
|
+
[onActiveProp, startingPoint]
|
82
|
+
);
|
83
|
+
const onEnd: TouchableHandlerProps['onEnd'] = useCallback(
|
84
|
+
(event) => {
|
85
|
+
const translationX = event.x - (startingPoint.current?.x ?? 0);
|
86
|
+
const translationY = event.y - (startingPoint.current?.y ?? 0);
|
87
|
+
return onEndProp?.({ ...event, translationX, translationY });
|
88
|
+
},
|
89
|
+
[onEndProp, startingPoint]
|
90
|
+
);
|
91
|
+
|
92
|
+
const isPointInPath = useCallback(
|
93
|
+
(point: Vector) => {
|
94
|
+
if (touchablePath) {
|
95
|
+
return unwrapAnimatedValue(touchablePath).contains(point.x, point.y);
|
96
|
+
}
|
97
|
+
|
98
|
+
const path = getSkiaPath(Component.name, props);
|
99
|
+
if (!path) {
|
100
|
+
throw Error('No touchablePath provided');
|
101
|
+
}
|
102
|
+
return path.contains(point.x, point.y);
|
103
|
+
},
|
104
|
+
[props, touchablePath]
|
105
|
+
);
|
106
|
+
|
107
|
+
useEffect(() => {
|
108
|
+
ref.current = {
|
109
|
+
[`id:${id}`]: {
|
110
|
+
isPointInPath,
|
111
|
+
onStart,
|
112
|
+
onActive,
|
113
|
+
onEnd,
|
114
|
+
},
|
115
|
+
...ref.current,
|
116
|
+
} as any;
|
117
|
+
|
118
|
+
return () => {
|
119
|
+
delete ref.current?.[id];
|
120
|
+
};
|
121
|
+
}, [id, isPointInPath, onActive, onEnd, onStart, ref, touchablePath]);
|
122
|
+
|
123
|
+
return Component(props as any);
|
124
|
+
};
|
125
|
+
};
|
126
|
+
|
127
|
+
export { withTouchableHandler };
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import {
|
2
|
+
ExtendedTouchInfo,
|
3
|
+
TouchInfo,
|
4
|
+
useValue,
|
5
|
+
} from '@shopify/react-native-skia';
|
6
|
+
import { useCallback } from 'react';
|
7
|
+
import type { TranslationInfo } from '../hoc';
|
8
|
+
|
9
|
+
type UseGestureHandlerParams<ContextType> = {
|
10
|
+
onStart?: (touchInfo: TouchInfo, context: ContextType) => void;
|
11
|
+
onActive?: (
|
12
|
+
extendedTouchInfo: ExtendedTouchInfo & TranslationInfo,
|
13
|
+
context: ContextType
|
14
|
+
) => void;
|
15
|
+
onEnd?: (
|
16
|
+
extendedTouchInfo: ExtendedTouchInfo & TranslationInfo,
|
17
|
+
context: ContextType
|
18
|
+
) => void;
|
19
|
+
};
|
20
|
+
|
21
|
+
const useGestureHandler = <ContextType>(
|
22
|
+
gestureHandlers: UseGestureHandlerParams<ContextType>
|
23
|
+
) => {
|
24
|
+
const { onStart, onActive, onEnd } = gestureHandlers;
|
25
|
+
|
26
|
+
const context = useValue<ContextType>({} as any);
|
27
|
+
|
28
|
+
const handleStart = useCallback(
|
29
|
+
(touchInfo: TouchInfo) => {
|
30
|
+
if (!onStart) return;
|
31
|
+
return onStart(touchInfo, context.current);
|
32
|
+
},
|
33
|
+
[context, onStart]
|
34
|
+
);
|
35
|
+
|
36
|
+
const handleActive = useCallback(
|
37
|
+
(extendedTouchInfo: ExtendedTouchInfo & TranslationInfo) => {
|
38
|
+
if (!onActive) return;
|
39
|
+
return onActive(extendedTouchInfo, context.current);
|
40
|
+
},
|
41
|
+
[context, onActive]
|
42
|
+
);
|
43
|
+
|
44
|
+
const handleEnd = useCallback(
|
45
|
+
(extendedTouchInfo: ExtendedTouchInfo & TranslationInfo) => {
|
46
|
+
if (!onEnd) return;
|
47
|
+
return onEnd(extendedTouchInfo, context.current);
|
48
|
+
},
|
49
|
+
[context, onEnd]
|
50
|
+
);
|
51
|
+
|
52
|
+
return {
|
53
|
+
onStart: handleStart,
|
54
|
+
onActive: handleActive,
|
55
|
+
onEnd: handleEnd,
|
56
|
+
};
|
57
|
+
};
|
58
|
+
|
59
|
+
export { useGestureHandler };
|
package/src/index.ts
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
import { rect, rrect, Skia, SkRect } from '@shopify/react-native-skia';
|
2
|
+
|
3
|
+
type GetRectPathParams =
|
4
|
+
| {
|
5
|
+
x: number;
|
6
|
+
y: number;
|
7
|
+
width: number;
|
8
|
+
height: number;
|
9
|
+
}
|
10
|
+
| { rect: SkRect };
|
11
|
+
|
12
|
+
const getRectPath = (params: GetRectPathParams) => {
|
13
|
+
const skPath = Skia.Path.Make();
|
14
|
+
if ('rect' in params) {
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
16
|
+
const { rect } = params;
|
17
|
+
|
18
|
+
skPath.addRect(rect);
|
19
|
+
return skPath;
|
20
|
+
}
|
21
|
+
const { x, y, width, height } = params;
|
22
|
+
skPath.addRect(rect(x, y, width, height));
|
23
|
+
return skPath;
|
24
|
+
};
|
25
|
+
|
26
|
+
type GetRoundedRectPathParams = GetRectPathParams & {
|
27
|
+
r: number;
|
28
|
+
};
|
29
|
+
|
30
|
+
const getRoundedRectPath = (params: GetRoundedRectPathParams) => {
|
31
|
+
const { r } = params;
|
32
|
+
if ('rect' in params) {
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
34
|
+
const { rect } = params;
|
35
|
+
return Skia.Path.Make().addRRect(rrect(rect, r, r));
|
36
|
+
}
|
37
|
+
const { x, y, width, height } = params;
|
38
|
+
return Skia.Path.Make().addRRect(rrect(rect(x, y, width, height), r, r));
|
39
|
+
};
|
40
|
+
|
41
|
+
export { getRectPath, getRoundedRectPath };
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import type { SkiaValue } from '@shopify/react-native-skia';
|
2
|
+
|
3
|
+
const unwrapAnimatedValue = <T>(value: SkiaValue<T> | T): T => {
|
4
|
+
if ((value as SkiaValue<T>).current != null) {
|
5
|
+
return (value as SkiaValue<T>).current;
|
6
|
+
}
|
7
|
+
return value as T;
|
8
|
+
};
|
9
|
+
|
10
|
+
const unwrapAnimatedValueObject = <T>(
|
11
|
+
value: Record<any, SkiaValue<T> | T>
|
12
|
+
): Record<any, T> => {
|
13
|
+
return Object.keys(value).reduce((acc, key) => {
|
14
|
+
return { ...acc, [key]: unwrapAnimatedValue(value[key]) };
|
15
|
+
}, {});
|
16
|
+
};
|
17
|
+
|
18
|
+
export { unwrapAnimatedValue, unwrapAnimatedValueObject };
|