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

Viewed 1809

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!

2 Answers

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?

Related