绘制多路径时 react-native-canvas 的性能优化

Performance optimization for react-native-canvas when drawing many paths

我想在我的 React Native 应用程序中镜像模拟段显示。段显示相当复杂,它由 100 多个不同的段组成。它包含三个 7 段数字显示和一个包含 20 个元素的进度条。其余的是自定义形状和符号,提供有关其所连接机器的当前状态的信息。我对 HTML canvas 有一些经验,并找到了 React Native 模块 react-native-canvas 并想尝试一下。但是,与我可以在网络浏览器中使用的 HTML canvas 相比,使用 react-native-canvas 似乎相当慢。

这是我的做法:

  1. 我在我的组件中导入模块:

import Canvas, {Image as CanvasImage, Path2D, ImageData} from 'react-native-canvas';

  1. 向我的渲染函数添加一个 canvas 元素:

<Canvas ref={this.handleCanvas}/>

  1. 存储对 canvas 的引用并设置其大小:
handleCanvas = (canvas) => {
    if (this.myCanvas === null && canvas !== null){
      canvas.width = 250;
      canvas.height = 250;
      this.myCanvas = canvas;
    }
  }
  1. 然后我可以为每个段调用一个 "draw" 绘制二维路径的函数:
draw(ctx){
  ctx.save();
  ctx.strokeStyle="#000000";
  ctx.lineWidth=2;
  ctx.lineJoin="round";
  ctx.font="   10px sans-serif";
  ctx.beginPath();
  ctx.moveTo(158.108112514019,24.324327290058136);
  ctx.lineTo(159.45946389436722,24.324327290058136);
  ctx.lineTo(160.13513958454132,25.67567867040634);
  ...
  ctx.lineTo(162.16216665506363,25.00000298023224);
  ctx.fill("nonzero");
  ctx.stroke();
  ctx.restore();
}

我得到这样的上下文: var ctx = this.myCanvas.getContext('2d');

我制作了一个有 13 个片段的原型。每个线段有大约 50 个节点,我一次绘制了所有 13 个线段。在我的 React Native 应用程序中,这需要将近一秒钟的时间来绘制,这太慢了(还有 90 多个片段我还没有渲染......)。如果我在 HTML canvas 和 Google Chrome 上绘制相同的路径,只需要 2-5 毫秒。

有没有人知道如何提高性能?或者是否有另一个库更适合我的目的?`

提前致谢!

感谢您发布如此详细的问题。 React Native Canvas 与 HTML canvas 相比相当慢,因为每个指令都被传送到 WebView。我能想到的一种提高性能的潜在方法是使用 Path2D 因为对象在呈现之前包含多个指令。你能试试看它是否提高了性能吗?

Iddans 的回答是正确的,因为我无法 post 作为对他的回答的评论来详细说明我们是如何解决这个问题的,我正在 post 自己一个新的答案。

正如 Iddan 所说,正确的解决方案确实是尽量减少发送到 canvas 的指令数量。 我们更改了问题的第 4 点。我们没有直接使用 ctx.lineTo(...) 语句绘制路径,而是将所有 SVG 路径提取为字符串并将它们存储在数组中:

const svgPaths = [
   'M713.33,497.34a38.67 ... ',
   ...
]

对于每个渲染周期,我们决定要渲染所有 SVG 路径的哪个子集并将它们存储在一个新数组中,然后我们创建所有所需 svg 路径的单个 Path2D 对象

const svgPathsSubset = [svgPaths[1], svgPaths[7], ... ]
const pathToRender = new Path2D(this.myCanvas, svgPathsSubset)
const ctx = this.myCanvas.getContext('2d')
ctx.fill(pathToRender)

这非常快,只需几毫秒。