-
@wcandillon has addressed this briefly in #2669, but I've had a lot of trouble migrating a simple drawing component using Here's the stripped-down version (removed anything unrelated to this issue) of the code for my component: import { useRef, useState, Children } from "react";
import { View } from "react-native";
import {
Path,
SkPath,
Skia,
useTouchHandler,
Canvas,
} from "@shopify/react-native-skia";
import { useTheme } from "styled-components";
const DrawingCanvas = () => {
const theme = useTheme();
const currentPath = useRef<SkPath | null>(null);
const [paths, setPaths] = useState<SkPath[]>([]);
const onTouch = useTouchHandler({
onStart: ({ x, y }) => {
currentPath.current = Skia.Path.Make();
currentPath.current.moveTo(x, y);
setPaths((prevState) => [...prevState, currentPath.current as SkPath]);
},
onActive: ({ x, y }) => {
currentPath.current?.lineTo(x, y);
},
});
return (
<View>
<Canvas onTouch={onTouch}>
{Children.toArray(paths.map((path) => <Path path={path} />))}
</Canvas>
</View>
);
};
export default DrawingCanvas; I tried using Latest attempt (does not work) import { useRef, useState, Children } from "react";
import { View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { runOnJS } from "react-native-reanimated";
import { Path, SkPath, Skia, Canvas } from "@shopify/react-native-skia";
const DrawingCanvas = () => {
const history = useRef<SkPath[]>([]);
const currentPath = useRef<SkPath | null>(null);
const [paths, setPaths] = useState<SkPath[]>([]);
const addPath = useCallback(
(x: number, y: number) => {
history.current = [];
currentPath.current = Skia.Path.Make();
currentPath.current.moveTo(x, y);
setPaths((prevState) => [...prevState, currentPath.current as SkPath]);
},
[setPaths, history, currentPath]
);
const updateCurrPath = useCallback(
(x: number, y: number) => {
currentPath.current?.lineTo(x, y);
},
[currentPath]
);
const drawGesture = Gesture.Pan()
.onStart(({ x, y }) => {
runOnJS(addPath)(x, y);
})
.onUpdate(({ x, y }) => {
runOnJS(updateCurrPath)(x, y);
})
return (
<View>
<GestureDetector gesture={pan}>
<Canvas>
{Children.toArray(paths.map((path) => <Path path={path} />))}
</Canvas>
</GestureDetector>
</View>
);
};
export default DrawingCanvas;
What is the recommended approach for managing a state like my Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
I arrived at this solution: import { useRef, useState, Children } from "react";
import { View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { runOnJS } from "react-native-reanimated";
import { Path, SkPath, Skia, Canvas } from "@shopify/react-native-skia";
const DrawingCanvas = () => {
const currentPath = useRef<SkPath | null>(null);
const [paths, setPaths] = useState<SkPath[]>([]);
const updatePaths = useCallback((newPath: SkPath) => {
setPaths((prevState) => [...prevState, newPath]);
}, []);
const drawGesture = Gesture.Pan()
.runOnJS(true)
.onBegin(({x, y}) => {
currentPath.current = Skia.Path.Make();
currentPath.current.moveTo(x, y);
runOnJS(updatePaths)(currentPath.current);
})
.onUpdate(({x, y}) => {
if (currentPath.current) {
currentPath.current.lineTo(x, y);
// Force a re-render to show the updated path (canvasRef.forceUpdate() didn't work for me)
setPaths((prev) => [...prev]);
}
});
return (
<View>
<GestureDetector gesture={pan}>
<Canvas>
{Children.toArray(paths.map((path) => <Path path={path} />))}
</Canvas>
</GestureDetector>
</View>
);
};
export default DrawingCanvas; Note: Appraised by @wcandillon as a good solution. |
Beta Was this translation helpful? Give feedback.
-
Hello Victor,
Sorry for the late reply, this looks great indeed. I hope this tutorial
would help as well: https://www.youtube.com/watch?v=5yM4NPcTwY4
Kind regards,
William
…On Wed, Jan 8, 2025 at 9:13 PM Victor Hofstetter ***@***.***> wrote:
Okay, so I'll leave this open, hoping for responses from someone on the
Skia team to give their take on the recommended approach, but this worked
for me:
import { useRef, useState, Children } from "react";import { View } from "react-native";import { Gesture, GestureDetector } from "react-native-gesture-handler";import { runOnJS } from "react-native-reanimated";import { Path, SkPath, Skia, Canvas } from ***@***.***/react-native-skia";
const DrawingCanvas = () => {
const currentPath = useRef<SkPath | null>(null);
const [paths, setPaths] = useState<SkPath[]>([]);
const updatePaths = useCallback((newPath: SkPath) => {
setPaths((prevState) => [...prevState, newPath]);
}, []);
const drawGesture = Gesture.Pan()
.runOnJS(true)
.onBegin(({x, y}) => {
currentPath.current = Skia.Path.Make();
currentPath.current.moveTo(x, y);
runOnJS(updatePaths)(currentPath.current);
})
.onUpdate(({x, y}) => {
if (currentPath.current) {
currentPath.current.lineTo(x, y);
// Force a re-render to show the updated path (canvasRef.forceUpdate() didn't work for me)
setPaths((prev) => [...prev]);
}
});
return (
<View>
<GestureDetector gesture={pan}>
<Canvas>
{Children.toArray(paths.map((path) => <Path path={path} />))}
</Canvas>
</GestureDetector>
</View>
);};
export default DrawingCanvas;
I assume this solution is sub-optimal because I didn't migrate anything to
run on the UI Thread, but it fixed my issue.
—
Reply to this email directly, view it on GitHub
<#2859 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACKXVQQCPEPDUHE2JXDCS32JWBFZAVCNFSM6AAAAABUSAQFD6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNZXHAYDIOI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***
com>
|
Beta Was this translation helpful? Give feedback.
I arrived at this solution: