react-native-skia icon indicating copy to clipboard operation
react-native-skia copied to clipboard

UI thread rendering

Open louix opened this issue 3 years ago • 2 comments

Description

Hey,

Apologies if this is already out there. Is there any plan to offer rendering off the main thread similar to Reanimated?

It would be handy for animations to continue when the JS thread is blocked (doing compute), especially for gestures.

Here is an example (code below). Panning on the x-axis uses Reanimated, and panning on the y-axis uses Skia. A blocking function runs every 3 seconds.

skia-blocking.webm

import * as skia from "@shopify/react-native-skia";
import { useEffect } from "react";
import { useWindowDimensions, View } from "react-native";
import { Gesture, GestureDetector, GestureHandlerRootView } from "react-native-gesture-handler";
import Animated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";

const block = () => {
  for (let i = 0; i < 50000000; i++) {}
};

export const Root = (): JSX.Element => {
  const { width, height } = useWindowDimensions();

  useEffect(() => {
    const i = setInterval(block, 3000);
    return () => clearInterval(i);
  }, []);

  const xPositionReanimated = useSharedValue(0);
  const yPositionReanimated = useSharedValue(0);

  const panGesture = Gesture.Pan().onChange((e) => {
    xPositionReanimated.value += e.changeX;
    yPositionReanimated.value += e.changeY;
  });
  const reanimatedStyle = useAnimatedStyle(() => ({
    translateX: xPositionReanimated.value,
  }));

  const skiaY = skia.useValue(0);
  skia.useSharedValueEffect(() => {
    skiaY.current = yPositionReanimated.value;
  }, yPositionReanimated);

  return (
    <View style={{ width, height }}>
      <GestureHandlerRootView>
        <GestureDetector gesture={panGesture}>
          <Animated.View style={reanimatedStyle}>
            <skia.Canvas style={{ height: height, width: width }}>
              <skia.Rect x={0} y={skiaY} height={100} width={100} color="red" />
            </skia.Canvas>
          </Animated.View>
        </GestureDetector>
      </GestureHandlerRootView>
    </View>
  );
};

louix avatar Aug 16 '22 11:08 louix

Simpler example only using Skia

const block = () => {
  for (let i = 0; i < 50000000; i++) {
  }
}

export default function App() {
  const { width, height } = useWindowDimensions();
  const animatedX = skia.useTiming({ loop: true, yoyo: true, from: 0, to: width - 100 })
  return (
    <View style={{ width, height }}>
      <skia.Canvas style={{ width: width, height: 300 }}>
          <skia.Rect x={animatedX} y={100} width={100} height={100} color="blue"/>
      </skia.Canvas>
      <Button title="Click to block the JS thread" onPress={() => block()}/>
    </View>
  );
}

louix avatar Aug 16 '22 11:08 louix

Hi @louix! Thanks for suggesting this :) It is not available yet - but this is definitely a feature we're looking forward to release :) We showed some examples of it in the latest video on @wcandillon 's channel and we're working hard to bring this to you all 🥳

chrfalch avatar Aug 16 '22 11:08 chrfalch

We are now rendering the UI thread but most animations need to be done via the JS thread at the moment, we are looking at the next steps there.

wcandillon avatar Nov 18 '22 13:11 wcandillon

As @wcandillon says, from version Release 0.1.159 beta we're rendering on the main thread - which should offer rendering on first frame - so I'm closing this issue :)

PS. Eventually we'll also start updating our computed animation values using workouts as well.

chrfalch avatar Nov 20 '22 10:11 chrfalch