Niya Panamdanam
twitter: @findniya
findniya.com
gethealthie.com
gethealthie.com
React JS to React Native
decay()
interpolate
clamp
useNativeThread
extrapolate
Declaritive vs Imperative
- React Native Reanimated
- React Native Animatable
- React Native Shared Elements
- Popmotions
- React Spring
- React Motion
- Animated API
- Layout Animations
UI Thread
JS Thread
Native Thread
BRIDGE
UI | kicks of the request for the animation to the JS Thread
JS Thread
Native Thread
BRIDGE
JS Thread
Native Thread
BRIDGE
UI Thread
UI Thread
UI Thread
JS Thread
Native Thread
BRIDGE
UI Thread
JS Thread
Native Thread
BRIDGE
JS| animation driver uses requestAnimationFrame to execute on every frame.
JS| intermediate values are calculated and passed to View component.
JS| View is updated using setNativeProps
JS| talks to the Native bridge
UI Thread
JS Thread
Native Thread
BRIDGE
UI Thread
JS Thread
Native Thread
BRIDGE
Native | updates the UI Thread
Frame drops are not a great user experience.
useNativeDriver
- This moves all the JS Thread steps to the Native Thread.
UI Thread
JS Thread
Native Thread
BRIDGE
- Animated API produces a graph of animated nodes, which are all sent once to the Native Thread.
- Now the Native Thread can directly update the UI Thread and skip the JS Thread in between.
Interpolation
Interpolation is a type of estimation, a method of finding new data points based on the range of a discrete set of known data points.
f(x)
0
100
?
https://www.youtube.com/watch?v=ybcL8e6ImSo&ab_channel=eveningkid
Interpolate Opacity
translationX 0 to 100
opacity 0 to 1
x
f(x)
0
100
0
1
50
?
Animated Api
const Loader = () => {
const translation = new Animated.Value(0)
// animating image
Animated.loop(
Animated.timing(
translation,
{
toValue: 100,
duration: 3000,
useNativeDriver: true
}
)
).start()
return (
<View style={styles.container}>
<Animated.Image
style={{
transform: [{translateX: translation}],
opacity: translation.interpolate({
inputRange: [0, 100],
outputRange: [0, 1]
})
}}
source={require('../../assets/images/cookie.png')}
/>
</View>
)
}
Extrapolation & Clamp
translation 0 to 100
opacity 0 to 1
Start to change opacity
when translation = 25
and end when translation = 75
25
75
50
translation
0
100
inputRange
outputRange
-
-
-
0
1
0.5
Extrapolation & Clamp
0
0
1
1
Clamp the left and right ranges
Animated Api
return (
<View style={styles.container}>
<Animated.Image
style={{...styles.image,
transform: [{translateX: translation}],
opacity: translation.interpolate({
inputRange: [25, 75],
outputRange: [0, 1]
//******ADD CLAMP****
extrapolate: 'clamp'
//*****
})
}}
source={require('../../assets/images/cookie.png')}
/>
</View>
)
React Spring: https://react-spring.io/
Moti: https://moti.fyi/
Reanimated (v2): https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation
- A physics-based animation library
- It is build on Animated and React Motion.
- Brought in the abilities of Animated and added spring motion and declarative methods of React Motion
- It works in web and mobile.
React Spring
const AnimatedView = animated(View)
const Loader = () => {
const springImage = useSpring({
from: {rotate: "0deg"},
to: {rotate: "360deg"},
loop: true,
config: {mass: 0.5, tension: 200, friction: 100},
})
return (
<View style={styles.container}>
<AnimatedView style={{...springImage, transform: [{rotate: springImage.rotate}]}}>
<Image
style={styles.image}
source={require('../../assets/images/cookie.png')}
/>
</AnimatedView>
</View>
)
}
UI Thread
JS Thread
Native Thread
BRIDGE
UI Thread
JS Thread
Native Thread
BRIDGE
- Built on Reanimated v2.
Abstracts the hooks to components that take an object.
JS Context
- Then adds more on top...
Combines the best of Framer Motion, React Spring and other libraries.
- Mobile/ React Native first, but also works on other platforms.
Moti
const Loader = () => {
return (
<View style={styles.container}>
<MotiImage
from={{
transform:[
{rotate: "0deg"}
]
}}
animate={{
transform:[
{rotate: '360deg'}
]
}}
transition={{
type: 'timing',
loop: true,
repeatReverse: false,
duration: 3000,
}}
style={styles.image}
source={require('../../assets/images/cookie.png')}
/>
</View>
)
}
Reanimated v2
const Loader: React.FC = () => {
const rotation = useSharedValue(0)
const animatedStyle = useAnimatedStyle(() => {
rotation.value = withRepeat(withTiming(360), 100, false)
return {
transform: [{rotate: `${rotation.value}deg`}]
}
}, [])
return (
<View style={styles.container}>
<Animated.View style={animatedStyle}>
<Image
style={styles.image}
source={require('../../assets/images/cookie.png')}
/>
</Animated.View>
<LoaderText/>
</View>
)
}