Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Last active September 24, 2025 16:51
Show Gist options
  • Save prof3ssorSt3v3/267fefde16a1184b8fbfe2a99dac168b to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/267fefde16a1184b8fbfe2a99dac168b to your computer and use it in GitHub Desktop.
React Native Gesture Handling and Animations Starter Files
//Reanimated v4
function App() {
  const sharedScale = useSharedValue(1); //1. create one or more shared values
  const sharedOpacity = useSharedValue(1);
  //2. create an animated style to be used by <Reanimated.View>
  const animatedStyle = useAnimatedStyles(() => {
    return {
      transform: [{ scale: withDelay(1500, withTiming(sharedScale.value, { duration: 800 })) }, { skewX: withTiming() }, { rotateZ: withTiming() }],
      opacity: withTiming(sharedOpacity.value, { duration: 2000 }),
    };
  });
  // withDelay, withTiming, withSequence
  //3. Add reanimated style to reanimated view
  //4. Add trigger(s) to change the shared value(s)
  return (
    <Reanimated.View style={[animatedStyle, styles.x]}>
      <Button
        onPress={() => {
          //run animation by changing the shared value(s)
          sharedScale.set(2);
          sharedOpacity.set(0.5);
        }}
        title="Click me"
      />
    </Reanimated.View>
  );
}
import { StatusBar } from 'expo-status-bar';
import { useRef } from 'react';
import { StyleSheet, Text, View, Button, Animated } from 'react-native';
export default function Animation() {
// const sv = useSharedValue(0);
const av = new Animated.Value(0);
// Animated.Value() is for a single value.
// Animated.ValueXY() will manage two values.
const myAnimatedValue = useRef(av).current;
const moveRight = () => {
Animated.timing(myAnimatedValue, {
toValue: 180,
duration: 500,
useNativeDriver: true,
}).start();
//when the moveRight function is called Animated will iterate through
//the possible values from 0 to 200 over the course of 500ms
//the start() method begins the iteration through the values.
//at the end, myAnimatedValue will have a current value of 200.
};
const moveLeft = () => {
Animated.timing(myAnimatedValue, {
toValue: -180,
delay: 500,
duration: 1000,
useNativeDriver: true,
}).start();
//reverse of the moveRight function except it takes a full second, and
//has a half second delay before starting.
//at the end myAnim will have a current value of 0
};
const reset = () => {
Animated.spring(myAnimatedValue, {
toValue: 0,
delay: 0,
duration: 800,
useNativeDriver: true,
}).start();
};
//There are Animated versions of View, Text, Image, ScrollView, FlatList, and SectionList
return (
<View style={styles.container}>
<Text>Animated</Text>
<Animated.View
style={{
// Bind paddingStart to animated value
// fontSize: myAnimatedValue,
transform: [
{ translateX: myAnimatedValue },
{ perspective: 1000 },
// without perspective, transform Animation will not render on Android while working fine on iOS
],
}}
>
<Text style={styles.txt}>Watch me move</Text>
</Animated.View>
<View>
<Button title="click to move right" onPress={moveRight} />
<Button title="click to move left" onPress={moveLeft} />
<Button title="reset position of text" onPress={reset} />
</View>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
box: {
padding: 10,
width: 200,
height: 200,
backgroundColor: 'hsl(120, 50%, 70%)',
},
txt: {
padding: 20,
margin: 20,
fontSize: 20,
},
});
import { StatusBar } from 'expo-status-bar';
// import { useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { GestureHandlerRootView, GestureDetector, Gesture } from 'react-native-gesture-handler';
import Reanimated, { useSharedValue, useAnimatedStyle } from 'react-native-reanimated';
export default function App() {
const offset = useSharedValue({ x: 0, y: 0 });
const start = useSharedValue({ x: 0, y: 0 });
const scale = useSharedValue(1);
const savedScale = useSharedValue(1);
const rotation = useSharedValue(0);
const savedRotation = useSharedValue(0);
const animatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: offset.value.x }, { translateY: offset.value.y }, { scale: scale.value }, { rotateZ: `${rotation.value}rad` }],
};
});
const dragGesture = Gesture.Pan()
.averageTouches(true)
.onUpdate((ev) => {
offset.value = {
x: ev.translationX + start.value.x,
y: ev.translationY + start.value.y,
};
})
.onEnd(() => {
start.value = {
x: offset.value.x,
y: offset.value.y,
};
});
const zoomGesture = Gesture.Pinch()
.onUpdate((ev) => {
scale.value = savedScale.value * ev.scale;
})
.onEnd(() => {
savedScale.value = scale.value;
});
const rotateGesture = Gesture.Rotation()
.onUpdate((ev) => {
rotation.value = savedRotation.value + ev.rotation;
})
.onEnd(() => {
savedRotation.value = rotation.value;
});
const composed = Gesture.Simultaneous(dragGesture, Gesture.Simultaneous(zoomGesture, rotateGesture));
return (
<GestureHandlerRootView>
<View style={styles.container}>
<GestureDetector gesture={composed}>
<Reanimated.View style={[styles.box, animatedStyles]}>
<Text>This is the animated.view</Text>
<Text>Inside the GestureDetector</Text>
</Reanimated.View>
</GestureDetector>
</View>
</GestureHandlerRootView>
);
}
// </GestureDetector>
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
box: {
padding: 10,
width: 200,
height: 200,
backgroundColor: 'hsl(120, 50%, 70%)',
},
});
import { StatusBar } from 'expo-status-bar';
import { useState } from 'react';
import { StyleSheet, Text, View, Pressable } from 'react-native';
import Reanimated, { useSharedValue, useAnimatedStyle, withTiming, withDelay, withRepeat, withSequence, FadeInRight, FadeOutLeft, Easing } from 'react-native-reanimated';
export default function Reanimation() {
const scaleXY = useSharedValue(1);
const [scale, setScale] = useState(1);
const animatedStyles = useAnimatedStyle(() => ({
transform: [{ scale: withTiming(scaleXY.value) }],
})); // jump immediately to the new value
const animatedStyles2 = useAnimatedStyle(() => {
return {
transform: [{ scale: withSequence(withRepeat(withDelay(1000, withTiming(scaleXY.value, { duration: 1000 })), 2), withTiming(0.6, { duration: 900 })) }],
};
}); //take 800ms to move to the new value
return (
<View style={styles.container}>
<Reanimated.View style={[styles.box, animatedStyles2]}>
<Text>This is the animated.view</Text>
<Text>{scale}</Text>
</Reanimated.View>
<View>
<Pressable
style={{ marginBlock: 20, paddingBlock: 20, paddingInline: 10, borderWidth: 1, borderRadius: 10, borderColor: 'steelblue', color: 'steelblue', backgroundColor: '#fff' }}
onPress={() => {
let num = Math.random() + 0.5; /* 0.5 to 1.4999 */
setScale(num);
scaleXY.set(num);
//increase the scale value by 10% with each tap
}}
>
<Text>Click to Animate</Text>
</Pressable>
</View>
{[0, 1, 2, 3, 4, 5].map((item) => (
<Reanimated.View
style={styles.entering}
entering={FadeInRight.delay(500 * item)
.duration(500)
.easing(Easing.inOut(Easing.quad))
.withInitialValues({ transform: [{ translateX: 1000 }] })}
exiting={FadeOutLeft}
key={`item_${item}`}
>
<Text>Animated Item {item + 1} On To Screen</Text>
</Reanimated.View>
))}
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
box: {
padding: 10,
width: 200,
height: 200,
backgroundColor: 'hsl(120, 50%, 70%)',
},
entering: {
backgroundColor: 'lavender',
paddingBlock: 10,
paddingInline: 20,
marginBlock: 5,
},
});
import { Text, StyleSheet, View, Pressable, Button } from 'react-native';
import { useRef } from 'react';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import Reanimated, { useAnimatedStyle } from 'react-native-reanimated';
function RightAction(prog, drag) {
const styleAnimation = useAnimatedStyle(() => {
console.log('showRightProgress:', prog.value);
console.log('appliedTranslation:', drag.value);
return {
transform: [{ translateX: drag.value + 50 }],
};
});
return (
<Reanimated.View style={styleAnimation}>
<Pressable
onPress={() => {
console.log('delete this item');
}}
>
<Text style={styles.rightAction}>Icon</Text>
</Pressable>
</Reanimated.View>
);
}
export default function Swiping() {
const reanimatedRef = useRef();
return (
<GestureHandlerRootView>
<View style={styles.container}>
<ReanimatedSwipeable containerStyle={[styles.swipeable, {}]} friction={2} rightThreshold={40} dragOffsetFromRightEdge={40} renderRightActions={RightAction} ref={reanimatedRef}>
<Text style={styles.txt}>Swipe me!</Text>
</ReanimatedSwipeable>
<Button
onPress={() => {
reanimatedRef.current.reset(); // no animation
// reanimatedRef.current.close(); //runs animation
//reanimatedRef.current!.openRight(); //runs animation
//reanimatedRef.current!.openLeft(); //runs animation
}}
title="Close"
/>
</View>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'stretch',
justifyContent: 'center',
},
rightAction: {
width: 50,
height: 50,
padding: 5,
backgroundColor: 'gold',
color: 'black',
},
separator: {
width: '100%',
borderTopWidth: 1,
},
txt: { color: 'white', paddingBlock: 10 },
swipeable: {
height: 50,
backgroundColor: 'rebeccapurple',
color: 'white',
alignItems: 'center',
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment