Ad

HTML5 Canvas Best Way To Plot A Bunch Of Resizable Graphs

I am currently creating some artsy background for my website with a bunch of randomly generated curve plots.

Demo

So far, I am using Html5 Canvas and I recreate the whole every time the window is resized to fit the new width of the canvas. The data all lies in x,y ~ [0,1],[0,1] and is then scaled to fit the window. This is how I set up the canvas transformation:

ctx.resetTransform();
ctx.scale(c.width, c.height);

Since the basic data does not change, I was hoping I could just adjust the canvas size (which deletes all data) and the transformation to update the plots (although I guess I might run into trouble concerning the stroke width, when stroking the lines in the [0,1] space). Is there any more efficient way for me to redraw all these plots (for example storing all the line-paths somehow) or do I really have to loop through everything again and recreate the whole scene?

Ad

Answer

Path2D

Use Path2D to create the content once then render to the scale as needed.

For example

const pathStroke = new Path2D(), pathFill = new Path2D();
var i;
for (i = 0; i <= 1; i += 1 / 100) {
    pathStroke.lineTo(i, Math.sin(i * Math.PI * 8) * 0.1 + 0.2);
    pathFill.lineTo(i, Math.sin(i * Math.PI * 16) * 0.1 + 0.4);
}
for (i = 0; i <= 1; i += 1 / 100) {
    pathFill.lineTo((1-i), Math.cos((i-i) * Math.PI * 13) * 0.1 + 0.5);
}

Then to render the content

ctx = canvas.getContext("2d");
ctx.setTransform(canvas.width, 0, 0, canvas.height, 0, 0);
ctx.stroke(pathStroke);
ctx.fill(pathFill);

Demo

Switch to full page to see content re-rendered and scaled.

  • The function createPaths adds content to the two paths pathStroke and pathFill
  • The function renderContent renders the content in the paths scaled to fit the canvas. The resize event just calls this function again.

const ctx = canvas.getContext("2d");
const pathStroke = new Path2D(), pathFill = new Path2D();
createPaths();
renderContent();
addEventListener("resize", renderContent);

function createPaths() {
  var i;
  for (i = 0; i <= 1; i += 1 / 1000) {
      pathStroke.lineTo(i * i, Math.sin(i * Math.PI * 8) * 0.05 + 0.1);
      pathFill.lineTo(i, Math.sin(i * Math.PI * 11) * 0.4 * i ** 0.5  + 0.6);
  }
  for (i = 0; i <= 1; i += 1 / 1000) {
      if (i === 0) {
          pathStroke.moveTo((1-i), Math.sin(i * i * Math.PI * 8) * (i * 0.1) + 0.2);      
      } else {
          pathStroke.lineTo((1-i), Math.sin(i * i * Math.PI * 9) * (i * 0.1) + 0.2);       
      }
      pathFill.lineTo((1-i), Math.cos((1-i) * Math.PI * 13) * 0.3 * i * i + 0.7);
  }
}


function renderContent() {
    canvas.width = innerWidth;
    canvas.height= innerHeight;
    
    // scale line width 2 pixels
    ctx.lineWidth = 2 / Math.max(canvas.width, canvas.height);

    ctx.setTransform(canvas.width, 0, 0, canvas.height, 0, 0);
    ctx.stroke(pathStroke);
    ctx.fill(pathFill);
}


    
body { padding: 0px }
canvas {
   position: absolute;
   top: 0px;
   left: 0px;
}
<canvas id="canvas"></canvas>

Ad
source: stackoverflow.com
Ad