简体   繁体   中英

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

I want to mirror an analog segment display in my React Native app. The segment display is quite complicated, it consists of more than 100 different segments. It contains three 7-segment displays for numbers and a progress bar with 20 elements. The rest are custom shapes and symbols that provide information about the current state of the machine it is attached to. I have some experience with the HTML canvas and found the React Native module react-native-canvas and wanted to give it a try. However, drawing on the react-native-canvas seems to be quite slow compared to the HTML canvas that I can use in a web-browser.

Here is what I do:

  1. I import the module in my component:

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

  1. Add a canvas element to my render function:

<Canvas ref={this.handleCanvas}/>

  1. store a reference to the canvas and set its size:
handleCanvas = (canvas) => {
    if (this.myCanvas === null && canvas !== null){
      canvas.width = 250;
      canvas.height = 250;
      this.myCanvas = canvas;
    }
  }
  1. Then i can call for each segment a "draw" function that draws a 2D-path:
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();
}

I get the context like this: var ctx = this.myCanvas.getContext('2d');

I made a prototype with 13 segments. Each segment has around 50 nodes and I draw all 13 segments at once. In my React Native app, this takes almost one second to draw which is way too slow (and there are 90 more segments that I do not render yet...). If I draw the same paths on a HTML canvas on Google Chrome, it only takes 2-5 milliseconds.

Does anyone have an idea, how I can improve the performance? Or is there another library that is more performant for my purposes?`

Thanks in advance!

Iddans answer was correct, since I can not post a detailed solution of how exactly we solved the problem as comment to his answer, I'm posting a new answer myself.

As Iddan stated, the correct solution was indeed to minimize the numbers of instructions sent to the canvas. We changed point 4 of my question. Instead of directly drawing a path with ctx.lineTo(...) statements, we extracted all SVG paths as strings and stored them in an array:

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

For each render cycle, we decide, what subset of all SVG paths we want to be rendered and store them in a new array, then we create a single Path2D object of all the desired svg Paths

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

This is very fast and takes just a few milliseconds.

thank you for posting your question with such detail. React Native Canvas is fairly slow compared to the HTML canvas as each instruction is being communicated to a WebView. One potential way to improve performance that I can think of is to use Path2D as the object holds multiple instructions before it renders. Can you try that and check if it improved the performance?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM