Last active
February 16, 2023 14:16
-
-
Save terrysahaidak/cf8c155c583f72acfcb2dbe5ea9bbe48 to your computer and use it in GitHub Desktop.
Reanimated Worklets explanation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { Text, View, Dimensions } from 'react-native'; | |
import Animated, { | |
useSharedValue, | |
useWorklet, | |
useEventWorklet, | |
} from 'react-native-reanimated'; | |
import { PanGestureHandler } from 'react-native-gesture-handler'; | |
import { getStatusBarHeight } from 'react-native-status-bar-height'; | |
function MichalApp() { | |
// those all are animated values | |
// but now they're connected to some background JS VM as well as the main one | |
// so this is kinda in-background-thread-executable animated values | |
// everything possible by TurboModules and JSI | |
const prevX = useSharedValue(0); | |
const prevY = useSharedValue(0); | |
const totalX = useSharedValue(0); | |
const totalY = useSharedValue(0); | |
const velocityX = useSharedValue(0); | |
const velocityY = useSharedValue(0); | |
const parentWidth = useSharedValue(Dimensions.get('window').width); | |
const parentHeight = useSharedValue( | |
Dimensions.get('window').height - getStatusBarHeight(true) | |
); | |
// this is kinda decay animation implementation | |
// you can only use values callback provides you | |
// nothing from the outside | |
// you're passing them as deps to this hook | |
const movable = useWorklet( | |
function(velocityX, velocityY, totalX, totalY, parentHeight, parentWidth) { | |
'worklet'; | |
// you're able to do any kind of stuff here | |
// everything will be run in background | |
// and that function fill be executed on each frame | |
// kinda "requestAnimationFrame" thing | |
// just some regular JS here | |
const cords = [ | |
{ | |
velocity: velocityX, | |
total: totalX, | |
dim: parentWidth, | |
}, | |
{ | |
velocity: velocityY, | |
total: totalY, | |
dim: parentHeight, | |
}, | |
]; | |
for (const cord of cords) { | |
const { velocity, total, dim } = cord; | |
if (Math.abs(velocity.value) > 0.01) { | |
// each shared value's got .value – getter of the stored value | |
// also "set" to set the new one | |
total.set(total.value + velocity.value / 60); | |
velocity.set(velocity.value * 0.99); | |
if (total.value < 0) { | |
total.set(-total.value); | |
velocity.set(-velocity.value); | |
} | |
if (total.value + 40 > dim.value) { | |
// and yes, you can create some temp variables | |
// just in the middle of execution | |
const excess = total.value + 40 - dim.value; | |
total.set(total.value - 2 * excess); | |
velocity.set(-velocity.value); | |
} | |
} | |
} | |
if ( | |
Math.abs(velocityX.value) < 0.01 && | |
Math.abs(velocityY.value) < 0.01 | |
) { | |
// not sure what this means | |
return true; | |
} | |
// you should pass all of the shared values to be able to use them | |
}, | |
[velocityX, velocityY, totalX, totalY, parentHeight, parentWidth] | |
); | |
// this is the event handler | |
// kinda Animated.event() with an arrow function instead of just mapping values expression | |
// so this event thing will be executed on each event | |
const worklet = useEventWorklet( | |
function(prevX, prevY, totalX, totalY, ruszable, velocityX, velocityY) { | |
'worklet'; | |
// event is available under the `this` | |
// so you can't use arrow functions | |
// this is basically gesture handler event | |
if (this.event.state === 2) { | |
prevX.set(totalX.value); | |
prevY.set(totalY.value); | |
// you have control all the worklets here | |
// so you can "stop" any worklet | |
this.stop(movable); | |
} | |
totalX.set(this.event.translationX + prevX.value); | |
totalY.set(this.event.translationY + prevY.value); | |
if (this.event.state === 5) { | |
velocityX.set(this.event.velocityX); | |
velocityY.set(this.event.velocityY); | |
// and run it, of course | |
// run means it's gonna be executed on each frame | |
this.start(movable); | |
} | |
}, | |
[prevX, prevY, totalX, totalY, movable, velocityX, velocityY] | |
); | |
return ( | |
<View style={{ flex: 1 }}> | |
<PanGestureHandler | |
// just as a regular event | |
onGestureEvent={worklet} | |
onHandlerStateChange={worklet}> | |
<Animated.View | |
style={{ | |
width: 40, | |
height: 40, | |
transform: [ | |
{ | |
// and regular values here | |
translateX: totalX, | |
}, | |
{ | |
translateY: totalY, | |
}, | |
], | |
backgroundColor: 'black', | |
}} | |
/> | |
</PanGestureHandler> | |
</View> | |
); | |
} | |
export default MichalApp; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment