Something awesome about Skia

03/17/2024 13:17, 2 years ago

React Native Skia works wonderfully.
I've managed to seamlessly integrate it with Reanimated 3 as well.
I even have the touch listeners directly on the Skia components.

It automatically overlays the Skia view with the Animated.View and binds it.
To achieve this, I created an extension of the skia canvas, along with a react context for the components to communicate the touch components to.

That in turn gives me this delicious setup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default function Page() {

  const onPress = () => {
    console.log('pressed');
  }

  return (
    <View style={{flex: 1}}>
      <Canvas>
        <Button onPress={onPress} />
      </Canvas>
    </View>
  );
}

This allows the same flexibility that the standard React components offer, in the sense that you can connect the onPress listener directly to a Skia component.
To me, this is just beautiful and will be one of the things that will soon allow me to fly very fast.

Thankful for Skia as well, that library performs so well.
Excited to see how far I can take this stack.

I'm considering doing Skia tutorials as well, in the same style as @wcandillon does along the way.
Let me know if this is something you would want.

So what happens under the hood?

All of this happens through a custom context and a custom hook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The hook, from a custom context
const { addGesture, removeGesture } = useGestureHandler()

// _layout.tsx
export default function RootLayout() {
  ...
  return (
    <VariousProviders>
      <Slot />
      {/* The gestures added by the components are rendered on top automatically */}
      { gestures.map((gesture, index) => (
        <Fragment key={index}>
          {gesture}
        </Fragment>
      ))}
    </VariousProviders>
  )
}


And then finally, the button component with touch handlers can be implemented like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default function Button({ onPress }: ButtonProps) {

  const { addGesture, removeGesture } = useGestureHandler()
  
  useEffect(() => {
    const gesture = <TabGesture 
      height={128} 
      width={128} 
      left={64} 
      top={64} 
      tabbed={tabbed} 
    />
    addGesture([gesture])
    return () => removeGesture([gesture])
  }, [])

  return (
    <>
      <Box box={rrect(rect(64, 64, 128, 128), 24, 24)} color='green' />
    </>
  )
}


Where the TabGesture component looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function TabGesture({top, left, width, height, tabbed}: TabGestureProps) {
  const gesture = Gesture.Manual()
    .onTouchesDown(() => {
      tabbed.value = true;
    })
    .onTouchesUp(() => {
      tabbed.value = false
    })

  const style = useAnimatedStyle(() => {
    return {height, width, top, left, position: 'absolute'}
  })

  return ( 
    <GestureDetector gesture={gesture}>
      <Animated.View style={style} />
    </GestureDetector>
  )
}


And that gives us this magical setup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default function Page() {

  const onPress = () => {
    console.log('pressed');
  }

  return (
    <View style={{flex: 1}}>
      <Canvas>
        <Button onPress={onPress} />
      </Canvas>
    </View>
  );
}


And then in the button, you can trigger the onPress event from the props like so (or however you want your component to trigger the event):

1
2
3
4
5
useDerivedValue(() => {
  if(tabbed.value) {
    runOnJS(onPress?.call)
  }
})


That's about it!

I hope you got inspired for your own Skia setup. Since all of these libraries are new, I find it interesting to see new uses of Skia pop up.
 
Thanks for reading 😊 Comments, likes and subscriptions are always appreciated, and can be done so annonymously.

2

5