user3241192 user3241192 - 5 months ago 24
Javascript Question

Simplify draw on canvas

This is my code.

Forget about some stuff outside the function (is just to make it clear on fiddle, it's a part of a bigger code.

Basically I have this part :

var drawDataSet = function (dataset) {
context.lineWidth = 1;
context.save();
context.beginPath();
var angle = (Math.PI) / (dataset.data.length / 2);
for (var i = 0; i < dataset.data.length; i++) {
var height = ((canvas.height / 2 * dataset.data[i]) / 100);
context.lineTo(Math.cos(angle * i) * height, -Math.sin(angle * i) * height);
}
var height = ((canvas.height / 2 * dataset.data[0]) / 100);
context.lineTo(height, 0);
context.closePath();
context.strokeStyle = dataset.strokeColor;
context.stroke();
context.fillStyle = dataset.fillColor;
context.fill();
context.fillStyle = dataset.pointfillColor;
context.fillStyle = dataset.pointStrokeColor;
for (var i = 0; i < dataset.data.length; i++) {
var height = ((canvas.height / 2 * dataset.data[i]) / 100);
drawPoint(Math.cos(angle * i) * height, -Math.sin(angle * i) * height, 3);
}
context.restore();
}

drawPoint = function (x,y,w) {
context.beginPath();
context.arc(x, y, w, 0, 2 * Math.PI, true);
context.stroke();
context.fill();
context.closePath();
}


I would like to know if there is an easier and better way to draw the same thing.

Actually I do the same exact loop 2 times at different position. This is because if I draw all in the same loop, everything breaks because the
closePath
closes all the paths. If I remove the
closePath
, it only breaks the fill and stroke.

Is it possible to put the
drawPoint
and the
drawLine
in the same loop?

Answer

Technically, Yes...

You can combine your draws in a single loop

You could draw your lines and points at the same time with a single loop by saving & restoring the context when you draw your current line and your current point. And some compositing must be done to keep your lines draw behind your points.

var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");

var points=[ {x:25,y:50},{x:100,y:25},{x:150,y:85},{x:125,y:100},{x:25,y:50}  ];
var lastX,lastY;

lastX=points[0].x;
lastY=points[0].y;

for(var i=1;i<points.length;i++){
  var p=points[i];
  drawLine(lastX,lastY,p.x,p.y,'green');
  drawPoint(p.x,p.y,3,'blue','red');
  lastX=p.x;
  lastY=p.y;
}


function drawLine(x0,y0,x1,y1,stroke){
  context.save();
  context.globalCompositeOperation='destination-over';
  context.lineWidth=2;
  context.beginPath();
  context.moveTo(x0,y0);
  context.lineTo(x1,y1);
  context.strokeStyle=stroke;
  context.stroke();
  context.restore();
}

function drawPoint(x,y,w,fill,stroke) {
  context.save();
  context.lineWidth=2;
  context.beginPath();
  context.arc(x, y, w, 0, 2 * Math.PI, true);
  context.strokeStyle=stroke;
  context.fillStyle=fill;
  context.stroke();
  context.fill();
  context.closePath();
  context.restore();
}
body{ background-color:white; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Alternatively, you could draw your lines & points in a single beginPath if you are willing to have just 1 color for both strokeStyle fillStyle.

But...performance is better if you draw the lines & points in separate loops

Saving & restoring the context is modestly expensive.

Since you are constantly saving & restoring, you get a in loss of performance versus doing 2 loops without save & restore.