You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native绘图应用:Skia与Gesture Handler实时绘制不更新问题

绘图应用实时绘制不显示,刷新后才出现的问题解决

问题描述

使用React Native结合react-native-skia和react-native-gesture-handler开发绘图应用时,遇到以下问题:手指在屏幕滑动绘制时,画布无任何内容显示;但刷新应用后,之前绘制的SVG路径会正确出现在对应位置。已尝试更换react-native-gesture-handler版本、将GestureHandlerRootView移至App.js,均未解决问题。


相关代码

Draw组件代码

import React, { useState } from "react";
import { View, Text } from "react-native";
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
  PanGestureHandler,
} from "../node_modules/react-native-gesture-handler";
import { Canvas, Path } from "@shopify/react-native-skia";

export default function Draw() {
  const [paths, setPaths] = useState([[]]);

  const pan = Gesture.Pan()
    .onStart((g) => {
      const newPaths = paths;
      newPaths.push(["M " + g.x + " " + g.y]);
      setPaths(newPaths);
    })
    .onUpdate((g) => {
        const newPaths = paths;
        newPaths[newPaths.length-1].push("L " + g.x + " " + g.y);
        setPaths(newPaths);
    })
    .minDistance(1)

  return (
       <GestureDetector gesture={pan}>
          <Canvas style={{flex:1}} collapsable={false}>
        {paths.map((p, index) => (
              <Path
                key={index}z
                path={p.join(" ")}
                strokeWidth={5}
                style="stroke"
                color="black"
              />
            ))}
          </Canvas>
      </GestureDetector>
  );
}

App.js代码

import React, { Component } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import ScoutingSheet from './screens/ScoutingSheet';
import QRCodeScanner from './screens/QRCodeScanner';
import Draw from './screens/AutonCanvas';
import {
  GestureHandlerRootView,
} from "./node_modules/react-native-gesture-handler";
const Stack = createNativeStackNavigator();

export default class App extends Component {
  render() {
    return (
      <GestureHandlerRootView style={{flex:1}}>
      <NavigationContainer>
        <Stack.Navigator screenOptions={{
          headerShown: false,
        }}>
          <Stack.Screen name="AutonCanvas" component={Draw} />
          <Stack.Screen name="ScoutingSheet" component={ScoutingSheet} />
          <Stack.Screen name="QrCodeScanner" component={QRCodeScanner} />
        </Stack.Navigator>
      </NavigationContainer>
      </GestureHandlerRootView>
    );
  }
}

package.json配置

{
  "name": "roboticsscoutingapp",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start --dev-client",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@react-native-async-storage/async-storage": "~1.17.3",
    "@react-native-community/cli-platform-android": "^10.2.0",
    "@react-navigation/native-stack": "^6.9.1",
    "@shopify/react-native-skia": "0.1.172",
    "expo": "^48.0.0",
    "expo-barcode-scanner": "~12.3.2",
    "expo-splash-screen": "~0.18.1",
    "expo-status-bar": "~1.4.0",
    "expo-updates": "~0.16.3",
    "i": "^0.3.7",
    "npm": "^9.6.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.71.3",
    "react-native-gesture-handler": "^2.9.0",
    "react-native-paper": "^4.12.5",
    "react-native-qrcode-svg": "^6.2.0",
    "react-native-reanimated": "^2.3.0",
    "react-native-simple-radio-button": "^2.7.4",
    "react-native-svg": "13.4.0",
    "react-native-table-component": "^1.2.2",
    "react-native-web": "~0.18.7",
    "react-navigation": "^4.4.4"
  },
  "devDependencies": {
    "@babel/core": "^7.18.6"
  },
  "private": true
}

问题原因

核心问题是直接修改了state中的数组引用,没有创建新的数组实例。React的状态更新依赖引用变化来检测状态变更:

  1. 执行const newPaths = paths时,只是将原数组的引用赋值给newPaths,并非创建新数组
  2. 后续的push操作都是在原state数组上修改,setPaths传递的还是同一个引用
  3. React会判定状态没有发生变化,因此不会触发组件重新渲染,Canvas也就不会实时更新内容
  4. 刷新应用时,state重新初始化并加载了之前修改后的数组,所以内容会显示

另外,原代码中Path组件的key属性后多了一个无效字符z(key={index}z),属于语法错误,也可能影响渲染。


修复方案

修改Draw组件中的状态更新逻辑,创建新的数组副本,同时修复语法错误:

import React, { useState } from "react";
import { View, Text } from "react-native";
import {
  Gesture,
  GestureDetector,
} from "react-native-gesture-handler"; // 改为直接从包导入,无需从node_modules路径引入
import { Canvas, Path } from "@shopify/react-native-skia";

export default function Draw() {
  const [paths, setPaths] = useState([[]]);

  const pan = Gesture.Pan()
    .onStart((g) => {
      // 解构原数组创建新副本,避免修改原state
      const newPaths = [...paths];
      newPaths.push(["M " + g.x + " " + g.y]);
      setPaths(newPaths);
    })
    .onUpdate((g) => {
      // 先复制外层数组,再复制当前正在绘制的路径数组,保证引用变化
      const newPaths = [...paths];
      const currentPath = [...newPaths[newPaths.length - 1]];
      currentPath.push("L " + g.x + " " + g.y);
      newPaths[newPaths.length - 1] = currentPath;
      setPaths(newPaths);
    })
    .minDistance(1);

  return (
    <GestureDetector gesture={pan}>
      <Canvas style={{ flex: 1 }} collapsable={false}>
        {paths.map((p, index) => (
          <Path
            key={index} // 去掉无效的z字符
            path={p.join(" ")}
            strokeWidth={5}
            style="stroke"
            color="black"
          />
        ))}
      </Canvas>
    </GestureDetector>
  );
}

额外优化点:

  • 无需从../node_modules路径导入react-native-gesture-handler的模块,直接从包名导入即可
  • 确保react-native-reanimated已正确配置(你的package.json中版本与RN 0.71.3兼容,无需额外调整)

内容的提问来源于stack exchange,提问作者Adik Nathan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:07:52