phaze
phaze

Reputation: 160

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?

Upvotes: 0

Views: 470

Answers (2)

Blindman67
Blindman67

Reputation: 54099

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>

Upvotes: 1

Sagar More
Sagar More

Reputation: 476

Since the data is not going to change why not use svg instead of having all the data first being saved in some place and having whole lot of logic being written to draw that on canvas.

There are sites available to convert you image to svg. All you have to do is make some changes which you seem most fit to your needs and you are done.

some examples: Trajan column

Upvotes: 0

Related Questions