Skip to content Skip to sidebar Skip to footer

HTML5 Canvas Paint Redrawing LineJoin Not Rounded

Hello i make like a paint with undo function, i write all coordinates in array and then try undo just redrawing without last coordinates but problem while i redrawing my canvas par

Solution 1:

I think that you're looking for ctx.lineCap property.
+ I modified your redraw function to use a switchinstead of your confusing if statements :

    function redraw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.lineCap = "round";
        ctx.beginPath();

        for(var i=0; i < points.length; i++) {
            var pt = points[i];                
            switch(pt.mode){
                    case "begin" : ctx.moveTo(pt.x, pt.y); 
                    case "draw" : ctx.lineTo(pt.x, pt.y);
                    case "end" : ctx.stroke();
            }
        }
}

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var points = [];
var size = 10;
var prevX = 0;
var prevY = 0;
var isCanDraw = false;
var rect = canvas.getBoundingClientRect();

$("#canvas").on("mousedown", function(e) {
  isCanDraw = true;

  prevX = e.clientX;
  prevY = e.clientY;
  
  points.push({
    x: prevX,
    y: prevY,
    size: size,
    mode: "begin"
  });
  ctx.beginPath();
  ctx.arc(prevX, prevY, size/2, 0, Math.PI*2);
  ctx.fill();
});

$("#canvas").on("mousemove", function(e) {
  if (isCanDraw) {
    stroke(e.clientX - rect.left, e.clientY - rect.top);
    points.push({
      x: prevX,
      y: prevY,
      size: size,
      mode: "draw"
    });
  }
});

$("#canvas").on("mouseup", function(e) {
  isCanDraw = false;
  points.push({
    x: prevX,
    y: prevY,
    size: size,
    mode: "end"
  });
});

$("#canvas").on("mouseleave", function(e) {
  isCanDraw = false;
});

$("#undo").on("click", function(e) {
  deleteLast();
  redraw();
});

function deleteLast() {
  if (points.length != 0) {
    var i = points.length - 1;
    while (points[i].mode != "begin") {
      i--;
      points.pop();
    }
    points.pop();
  }
}

function redraw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.lineCap = "round";
  ctx.beginPath();

  for (var i = 0; i < points.length; i++) {
    var pt = points[i];
    switch (pt.mode) {
      case "begin":
        ctx.moveTo(pt.x, pt.y);
      case "draw":
        ctx.lineTo(pt.x, pt.y);
      case "end":
        ctx.stroke();
    }
  }
}


function stroke(x, y) {
  ctx.lineWidth = size;
  ctx.lineJoin = "round";

  ctx.beginPath();
  ctx.moveTo(prevX, prevY);
  ctx.lineTo(x, y);
  ctx.closePath();
  ctx.stroke();
  prevX = x;
  prevY = y;
}
// debounce our rect update func
var scrolling = false;
function scrollHandler(){
  rect = canvas.getBoundingClientRect();
  scrolling = false;
  }
$(window).on('scroll resize', function(e){
  if(!scrolling){
    requestAnimationFrame(scrollHandler);
    }
 });
#canvas {
  border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
<input type="button" id="undo" value="undo">

Solution 2:

@Kaiido answers correctly that applying lineCap='round' will round the ends of your redrawn line.

Two further thoughts:

  1. You should account for your canvas's offset position from the top-left corner of the document. Otherwise your prevX & prevY positions will be slightly off if the canvas is not on the top-left of the document.

  2. You will have a more crisp line (less "bulgy") if you allow only the beginning and ending linecaps to be rounded and all the interim linecaps to be butted. Leave the linejoins as the default of mitered.

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;

var points = []; 
var size = 10;
var prevX = 0;
var prevY = 0;
var isCanDraw = false;

$("#canvas").on("mousedown", function(e) {
  isCanDraw = true;

  prevX = e.clientX-offsetX;
  prevY = e.clientY-offsetY;
  points.push({x: prevX, y: prevY, size: size, mode: "begin"});
});

$("#canvas").on("mousemove", function(e) {
  if(isCanDraw) {
    stroke(e.clientX-offsetX, e.clientY-offsetY);
    points.push({x: prevX, y: prevY, size: size, mode: "draw"});
  }     
});

$("#canvas").on("mouseup", function(e) {
  isCanDraw = false;  
  points.push({x: prevX, y: prevY, size: size, mode: "end"});
});

$("#canvas").on("mouseleave", function(e) {
  isCanDraw = false; 
});

$("#undo").on("click", function(e) {
  deleteLast();
  redraw();
});

function deleteLast() {
  if(points.length != 0) {
    var i = points.length - 1;
    while(points[i].mode !== "begin") {
      i--; 
      points.pop();
    }
    points.pop();
  }
}


function redraw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  var savedFillStyle=ctx.fillStyle;
  ctx.fillStyle=ctx.strokeStyle;

  var i=0;
  while(i<points.length){

    var p=points[i];

    // draw "begin" as circle instead of line
    ctx.beginPath();
    ctx.arc(p.x,p.y,p.size/2,0,Math.PI*2);
    ctx.closePath();
    ctx.fill();

    // draw "draw"
    ctx.lineWidth=p.size;
    ctx.beginPath();
    ctx.moveTo(p.x,p.y);
    i++;
    while(i<points.length && points[i].mode!='end'){
      var p=points[i];
      ctx.lineTo(p.x,p.y);
      i++;
    }
    ctx.stroke();

    // draw "end" as circle instead of line
    var p=points[i];
    ctx.beginPath();
    ctx.arc(p.x,p.y,p.size/2,0,Math.PI*2);
    ctx.closePath();
    ctx.fill();

    i++;
  }

  ctx.fillStyle=savedFillStyle;

}

function stroke(x,y) {
  ctx.lineWidth = size;
  ctx.lineJoin = "round";

  ctx.beginPath();
  ctx.moveTo(prevX, prevY);
  ctx.lineTo(x, y);
  ctx.closePath();
  ctx.stroke();
  prevX = x;
  prevY = y;
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
<input type="button" id="undo" value="undo">    </body>

Post a Comment for "HTML5 Canvas Paint Redrawing LineJoin Not Rounded"