Skip to content

Commit faa47c1

Browse files
committed
android implementation
1 parent 592c9c7 commit faa47c1

1 file changed

Lines changed: 83 additions & 26 deletions

File tree

example/src/pages/SharedValueListenerExample.tsx

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
1+
import {
2+
View,
3+
Text,
4+
StyleSheet,
5+
Button,
6+
ActivityIndicator,
7+
} from 'react-native';
28
import { type Metadata } from '../helpers/metadata';
39
import Animated, {
410
useSharedValue,
511
useAnimatedReaction,
612
useAnimatedStyle,
713
withSpring,
8-
withTiming,
914
} from 'react-native-reanimated';
1015
import {
1116
Gesture,
1217
GestureDetector,
1318
GestureHandlerRootView,
1419
} from 'react-native-gesture-handler';
15-
import { useMemo } from 'react';
16-
import { NitroModules } from 'react-native-nitro-modules';
20+
import { useCallback, useEffect, useMemo } from 'react';
21+
import { NitroModules, type BoxedHybridObject } from 'react-native-nitro-modules';
1722
import {
1823
Fit,
1924
RiveView,
2025
useRiveFile,
2126
type RiveFile,
27+
type RiveViewRef,
2228
type ViewModelInstance,
2329
type ViewModelNumberProperty,
2430
} from '@rive-app/react-native';
@@ -68,8 +74,9 @@ function AnimatedRiveExample({
6874
instance: ViewModelInstance;
6975
file: RiveFile;
7076
}) {
71-
const pressed = useSharedValue(false);
72-
const offset = useSharedValue(0);
77+
const progress = useSharedValue(0);
78+
const startY = useSharedValue(0);
79+
const viewRef = useSharedValue<BoxedHybridObject<RiveViewRef> | null>(null);
7380

7481
const boxedProperty = useMemo(() => {
7582
const posYProperty = instance.numberProperty('posY');
@@ -79,39 +86,66 @@ function AnimatedRiveExample({
7986
return NitroModules.box(posYProperty);
8087
}, [instance]);
8188

82-
const pan = Gesture.Pan()
83-
.onBegin(() => {
84-
pressed.value = true;
85-
})
86-
.onChange((event) => {
87-
offset.value = event.translationY * 3;
88-
})
89-
.onFinalize(() => {
90-
offset.value = withSpring(0);
91-
pressed.value = false;
92-
});
93-
9489
useAnimatedReaction(
95-
() => offset.value,
90+
() => progress.value,
9691
(value: number) => {
9792
'worklet';
9893
if (!boxedProperty) return;
99-
const property = boxedProperty.unbox() as ViewModelNumberProperty;
94+
const property = boxedProperty.unbox();
10095
property.value = value;
96+
97+
viewRef.value?.unbox()?._playIfNeeded();
10198
},
10299
[boxedProperty]
103100
);
104101

102+
const panGesture = Gesture.Pan()
103+
.onStart(() => {
104+
'worklet';
105+
startY.value = progress.value;
106+
})
107+
.onUpdate((event) => {
108+
'worklet';
109+
progress.value = startY.value + event.translationY * 3;
110+
})
111+
.onEnd((event) => {
112+
'worklet';
113+
// Use velocity from gesture to set initial velocity of spring
114+
progress.value = withSpring(progress.value > 400 ? 800 : 0, {
115+
damping: 10,
116+
stiffness: 100,
117+
velocity: event.velocityY * 3,
118+
});
119+
});
120+
105121
const circleStyle = useAnimatedStyle(() => ({
106-
transform: [{ translateY: offset.value / 3 }],
107-
backgroundColor: pressed.value ? '#FFE04B' : 'blue',
108-
scale: withTiming(pressed.value ? 1.2 : 1),
122+
transform: [{ translateY: progress.value / 3 }],
109123
}));
110124

125+
const animateTo800 = useCallback(() => {
126+
progress.value = 0;
127+
progress.value = withSpring(800, {
128+
damping: 8,
129+
stiffness: 80,
130+
});
131+
}, [progress]);
132+
133+
const animateTo0 = () => {
134+
progress.value = withSpring(0, {
135+
damping: 8,
136+
stiffness: 80,
137+
});
138+
};
139+
140+
useEffect(() => {
141+
animateTo800();
142+
}, [animateTo800]);
143+
111144
return (
112145
<GestureHandlerRootView style={styles.container}>
113146
<Text style={styles.subtitle}>
114-
Drag the blue circle to move both circles. Release to spring back.
147+
Drag the blue circle to control position. Release to spring with
148+
velocity. (Red = Rive, Blue = React Native)
115149
</Text>
116150

117151
<View style={styles.riveContainer}>
@@ -122,11 +156,21 @@ function AnimatedRiveExample({
122156
fit={Fit.Layout}
123157
layoutScaleFactor={1}
124158
file={file}
159+
hybridRef={{
160+
f: (ref) => {
161+
viewRef.value = NitroModules.box(ref);
162+
},
163+
}}
125164
/>
126-
<GestureDetector gesture={pan}>
165+
<GestureDetector gesture={panGesture}>
127166
<Animated.View style={[styles.blueCircle, circleStyle]} />
128167
</GestureDetector>
129168
</View>
169+
170+
<View style={styles.buttonContainer}>
171+
<Button title="Bounce to 800" onPress={animateTo800} />
172+
<Button title="Bounce to 0" onPress={animateTo0} />
173+
</View>
130174
</GestureHandlerRootView>
131175
);
132176
}
@@ -141,11 +185,18 @@ const styles = StyleSheet.create({
141185
flex: 1,
142186
backgroundColor: '#fff',
143187
},
188+
title: {
189+
fontSize: 24,
190+
fontWeight: 'bold',
191+
textAlign: 'center',
192+
marginTop: 20,
193+
marginBottom: 10,
194+
},
144195
subtitle: {
145196
fontSize: 16,
146197
color: '#666',
147198
textAlign: 'center',
148-
marginVertical: 20,
199+
marginBottom: 20,
149200
paddingHorizontal: 20,
150201
},
151202
riveContainer: {
@@ -162,6 +213,12 @@ const styles = StyleSheet.create({
162213
textAlign: 'center',
163214
padding: 20,
164215
},
216+
buttonContainer: {
217+
flexDirection: 'row',
218+
justifyContent: 'center',
219+
gap: 20,
220+
padding: 20,
221+
},
165222
blueCircle: {
166223
position: 'absolute',
167224
left: 50,

0 commit comments

Comments
 (0)