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

How can I update Path without rerendering

Open doublelam opened this issue 1 year ago • 3 comments

Description

const [path, setPath] = useState<SkPath>(Skia.Path.Make());

const panGesture = useMemo(
    () =>
      Gesture.Pan()
        .onBegin(e => {
          'worklet';

          const coords = getRelativeCoords(viewRef as any, e.absoluteX, e.absoluteY);
          path.moveTo(coords?.x||0, coords?.y||0)
        })
        .onUpdate(e => {
          'worklet';

          const coords = getRelativeCoords(viewRef as any, e.absoluteX, e.absoluteY);
          path.lineTo(coords?.x||0, coords?.y||0);
        })
    [],
  );

return (
    <GestureDetector gesture={panGesture}>
      <View ref={viewRef} style={styles.canvas}>
        <Canvas style={{flex: 1}}>
          {/* Drawing path */}
            <Path
              path={path}
              // style="stroke"
              color={strokeColor}
              stroke={{width: typeof strokeWidth === 'number' ? strokeWidth : strokeWidth.value}}
            />
        </Canvas>
      </View>
    </GestureDetector>
  );

it doesn't work. how can I change Path without rerendering?

Version

1.3.11

Steps to reproduce

Above code

Snack, code example, screenshot, or link to a repository

no

doublelam avatar Sep 17 '24 10:09 doublelam

@doublelam Use sharedValue if you don't want to rerender entire screen:

const currentPath = useSharedValue(Skia.Path.Make());
const panGesture = useMemo(
    () =>
      Gesture.Pan()
        .onBegin(e => {
          'worklet';
          currentPath.modify((v) => {
                const coords = getRelativeCoords(viewRef as any, e.absoluteX, e.absoluteY);

                v.lineTo(coords.x || 0, coords.y || 0);
                return v;
            });
        })
        .onUpdate(e => {
          'worklet';

          const coords = getRelativeCoords(viewRef as any, e.absoluteX, e.absoluteY);
          currentPath.modify((v) => {
                const coords = getRelativeCoords(viewRef as any, e.absoluteX, e.absoluteY);

                v.lineTo(coords.x || 0, coords.y || 0);
                return v;
            });       
          })
    [],
  );
..... // next code stays same

mrEuler avatar Sep 18 '24 21:09 mrEuler

@wcandillon issue can be closed

mrEuler avatar Sep 18 '24 21:09 mrEuler

Ok now it works, but I need to use derived value or it does not work

 const derived = useDerivedValue(() => {
  return pathSharedVal.value.toSVGString()
});

Thank you!

doublelam avatar Sep 19 '24 09:09 doublelam

@mrEuler thanks for the insights, Why am I still unable to draw rects based on shared values? Why it requires a rerendering?

import React from "react";
import { Canvas, Rect } from "@shopify/react-native-skia";
import { useSharedValue } from "react-native-reanimated";
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from "react-native-gesture-handler";

const AnimatedPathCanvas = () => {
  const points = useSharedValue([{ x: 50, y: 50 }]); // Initial point

  const panGesture = Gesture.Pan()
    .onBegin((e) => {
      "worklet";
      points.modify((v) => {
        v.push({ x: e.x, y: e.y });
        return v;
      });
    })
    .onUpdate((e) => {
      "worklet";
      points.modify((v) => {
        v.push({ x: e.x, y: e.y });
        return v;
      });
    });
 
  return (
    <GestureHandlerRootView>
      <GestureDetector gesture={panGesture}>
        <Canvas style={{ flex: 1, backgroundColor: "yellow" }}>
          {/* Drawing Rect */}
          {points?.value?.map((point, index) => (
            <Rect
              key={index} 
              x={point.x}
              y={point.y}
              width={2.5}
              height={2.5}
              color="blue"
            />
          ))}
        </Canvas>
      </GestureDetector>
    </GestureHandlerRootView>
  );
};

export default AnimatedPathCanvas;

msriaz avatar Dec 08 '24 04:12 msriaz