Skip to content

Commit a5c00bc

Browse files
committed
first examples
1 parent 679ab46 commit a5c00bc

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

apps/common-app/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ import ManualExample from './src/simple/manual';
8282
import SimpleFling from './src/simple/fling';
8383

8484
import { Icon } from '@swmansion/icons';
85+
import Lock from './src/v3_api/lock/lock';
86+
import V3Fling from './src/v3_api/fling/fling';
87+
import LogicDetectorExample from './src/v3_api/svg/svg';
8588

8689
interface Example {
8790
name: string;
@@ -98,6 +101,14 @@ const EXAMPLES: ExamplesSection[] = [
98101
sectionTitle: 'Empty',
99102
data: [{ name: 'Empty Example', component: EmptyExample }],
100103
},
104+
{
105+
sectionTitle: 'V3 api',
106+
data: [
107+
{ name: 'V3 Fling', component: V3Fling },
108+
{ name: 'Svg', component: LogicDetectorExample },
109+
{ name: 'Lock', component: Lock },
110+
],
111+
},
101112
{
102113
sectionTitle: 'New api',
103114
data: [
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import { StyleSheet, View } from 'react-native';
3+
import {
4+
Directions,
5+
NativeDetector,
6+
useFling,
7+
} from 'react-native-gesture-handler';
8+
import Animated, {
9+
useSharedValue,
10+
useAnimatedStyle,
11+
withTiming,
12+
Easing,
13+
} from 'react-native-reanimated';
14+
15+
export default function V3Fling() {
16+
const position = useSharedValue(0);
17+
const beginPosition = useSharedValue(0);
18+
19+
const flingGesture = useFling({
20+
direction: Directions.LEFT | Directions.RIGHT,
21+
onBegin: (e) => {
22+
'worklet';
23+
beginPosition.value = e.handlerData.x;
24+
},
25+
onStart: (e) => {
26+
'worklet';
27+
const direction = Math.sign(e.handlerData.x - beginPosition.value);
28+
position.value = withTiming(position.value + direction * 50, {
29+
duration: 300,
30+
easing: Easing.bounce,
31+
});
32+
},
33+
});
34+
35+
const animatedStyle = useAnimatedStyle(() => ({
36+
transform: [{ translateX: position.value }],
37+
}));
38+
39+
return (
40+
<View style={styles.centerView}>
41+
<NativeDetector gesture={flingGesture}>
42+
<Animated.View style={[styles.box, animatedStyle]} />
43+
</NativeDetector>
44+
</View>
45+
);
46+
}
47+
48+
const styles = StyleSheet.create({
49+
centerView: {
50+
flex: 1,
51+
justifyContent: 'center',
52+
alignItems: 'center',
53+
},
54+
box: {
55+
height: 120,
56+
width: 120,
57+
backgroundColor: '#b58df1',
58+
marginBottom: 30,
59+
},
60+
});
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import React, { useState } from 'react';
2+
import { View, Text, StyleSheet } from 'react-native';
3+
import Animated, {
4+
useSharedValue,
5+
useAnimatedStyle,
6+
withTiming,
7+
runOnJS,
8+
} from 'react-native-reanimated';
9+
import {
10+
NativeDetector,
11+
useExclusive,
12+
useLongPress,
13+
usePinch,
14+
useRotation,
15+
useSimultaneous,
16+
useTap,
17+
} from 'react-native-gesture-handler';
18+
19+
export default function Lock() {
20+
const rotation = useSharedValue(-Math.PI / 2);
21+
const savedRotation = useSharedValue(-Math.PI / 2);
22+
23+
const scale = useSharedValue(0.6);
24+
const savedScale = useSharedValue(1);
25+
26+
const [locked, setLocked] = useState(true);
27+
const snapThreshold = 0.4;
28+
const scaleThreshold = 0.1;
29+
const minScale = 0.5;
30+
const maxScale = 1;
31+
const TWO_PI = 2 * Math.PI;
32+
33+
// Tap to lock
34+
const tap = useTap({
35+
onEnd: () => {
36+
'worklet';
37+
if (savedRotation.value === 0 && scale.value === maxScale) {
38+
runOnJS(setLocked)(false);
39+
}
40+
},
41+
});
42+
43+
// Long press to cancel tap
44+
const longPress = useLongPress({});
45+
46+
const confirm = useExclusive(longPress, tap);
47+
48+
const rotationGesture = useRotation({
49+
onUpdate: (e) => {
50+
'worklet';
51+
rotation.value = savedRotation.value + e.handlerData.rotation;
52+
53+
if (!locked) {
54+
runOnJS(setLocked)(true);
55+
}
56+
},
57+
onEnd: () => {
58+
'worklet';
59+
60+
const nearestMultiple = Math.round(rotation.value / TWO_PI) * TWO_PI;
61+
62+
if (Math.abs(rotation.value - nearestMultiple) < snapThreshold) {
63+
rotation.value = withTiming(nearestMultiple, { duration: 300 });
64+
savedRotation.value = 0;
65+
} else {
66+
rotation.value = withTiming(-Math.PI / 2, { duration: 300 });
67+
savedRotation.value = -Math.PI / 2;
68+
}
69+
},
70+
simultaneousWithExternalGesture: confirm,
71+
});
72+
73+
const pinchGesture = usePinch({
74+
onUpdate: (e) => {
75+
'worklet';
76+
const value = savedScale.value * e.handlerData.scale;
77+
if (value < minScale || value > maxScale) {
78+
return;
79+
}
80+
scale.value = value;
81+
82+
if (!locked) {
83+
runOnJS(setLocked)(true);
84+
}
85+
},
86+
onEnd: () => {
87+
'worklet';
88+
89+
if (Math.abs(scale.value - maxScale) < scaleThreshold) {
90+
scale.value = withTiming(maxScale, { duration: 300 });
91+
} else {
92+
scale.value = withTiming(minScale, { duration: 300 });
93+
}
94+
savedScale.value = scale.value;
95+
},
96+
simultaneousWithExternalGesture: confirm,
97+
});
98+
99+
const unlockingGesture = useSimultaneous(rotationGesture, pinchGesture);
100+
101+
const animatedStyle = useAnimatedStyle(() => ({
102+
transform: [
103+
{ rotateZ: `${(rotation.value / Math.PI) * 180}deg` },
104+
{ scale: scale.value },
105+
],
106+
}));
107+
108+
return (
109+
<View style={styles.container}>
110+
<View style={styles.outerBox}>
111+
<NativeDetector gesture={unlockingGesture}>
112+
<Animated.View style={[styles.box, animatedStyle]}>
113+
<NativeDetector gesture={confirm}>
114+
<Text style={styles.lockIcon}>{locked ? '🔒' : '🔓'}</Text>
115+
</NativeDetector>
116+
</Animated.View>
117+
</NativeDetector>
118+
</View>
119+
<Text>{locked ? 'Locked' : 'Unlocked!'}</Text>
120+
<Text style={styles.instructions}>
121+
Tou unlock rotate 90 degrees clockwise, and scale to fill the square.
122+
Then tap to confirm, longpress to cancel the tap
123+
</Text>
124+
</View>
125+
);
126+
}
127+
128+
const styles = StyleSheet.create({
129+
container: {
130+
flex: 1,
131+
alignItems: 'center',
132+
justifyContent: 'center',
133+
},
134+
lockIcon: {
135+
fontSize: 40,
136+
color: '#fff',
137+
fontWeight: 'bold',
138+
},
139+
instructions: {
140+
color: '#9CA3AF',
141+
marginTop: 8,
142+
textAlign: 'center',
143+
paddingHorizontal: 16,
144+
},
145+
146+
outerBox: {
147+
height: 200,
148+
width: 200,
149+
backgroundColor: 'gray',
150+
borderRadius: 20,
151+
alignItems: 'center',
152+
justifyContent: 'center',
153+
marginBottom: 50,
154+
},
155+
box: {
156+
height: 200,
157+
width: 200,
158+
backgroundColor: '#b58df1',
159+
borderRadius: 20,
160+
alignItems: 'center',
161+
justifyContent: 'center',
162+
},
163+
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import { Text, View, StyleSheet } from 'react-native';
3+
import {
4+
NativeDetector,
5+
LogicDetector,
6+
useTap,
7+
} from 'react-native-gesture-handler';
8+
9+
import Svg, { Circle, Rect } from 'react-native-svg';
10+
11+
export default function LogicDetectorExample() {
12+
const circleElementTap = useTap({
13+
onStart: () => {
14+
'worklet';
15+
console.log('clicked circle');
16+
},
17+
});
18+
const rectElementTap = useTap({
19+
onStart: () => {
20+
'worklet';
21+
console.log('clicked parallelogram');
22+
},
23+
});
24+
const containerTap = useTap({
25+
onStart: () => {
26+
'worklet';
27+
console.log('clicked container');
28+
},
29+
});
30+
31+
return (
32+
<View>
33+
<View style={styles.container}>
34+
<Text style={styles.header}>
35+
Overlapping SVGs with gesture detectors
36+
</Text>
37+
<View style={{ backgroundColor: 'tomato' }}>
38+
<NativeDetector gesture={containerTap}>
39+
<Svg height="250" width="250">
40+
<LogicDetector gesture={circleElementTap}>
41+
<Circle cx="125" cy="125" r="125" fill="green" />
42+
</LogicDetector>
43+
<LogicDetector gesture={rectElementTap}>
44+
<Rect skewX="45" width="125" height="250" fill="yellow" />
45+
</LogicDetector>
46+
</Svg>
47+
</NativeDetector>
48+
</View>
49+
<Text>
50+
Tapping each color should read to a different console.log output
51+
</Text>
52+
</View>
53+
</View>
54+
);
55+
}
56+
57+
const styles = StyleSheet.create({
58+
container: {
59+
alignItems: 'center',
60+
justifyContent: 'center',
61+
marginBottom: 48,
62+
},
63+
header: {
64+
fontSize: 18,
65+
fontWeight: 'bold',
66+
margin: 10,
67+
},
68+
});

0 commit comments

Comments
 (0)