HTML5 canvas 绘制重绘线连接不圆角

HTML5 canvas paint redrawing lineJoin not rounded

你好,我就像一个带有撤消功能的油漆,我将所有坐标写在数组中,然后尝试撤消,只是在没有最后一个坐标的情况下重绘,但是当我重绘我的 canvas 参数不正确时出现问题。我使用 lineJoin = "roind" 但重绘后我看到没有圆..

我绘制时圆线开始和线结束的结果:

撤消功能后没有圆头和行尾的结果:

在逐个坐标重绘所有图纸时,我不知道我的圆线在哪里消失了..

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

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

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

    });

    $("#canvas").on("mousemove", function(e) {
        if(isCanDraw) {
            stroke(e.clientX, e.clientY);
            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);

            if(points.length != 0) {
                for(var i=0; i < points.length; i++) {
                    var pt = points[i];

                    var begin=false;
                    if(size != pt.size) {
                        size = pt.size;
                        begin=true;
                    }
                  
                    if(pt.mode == "begin" || begin) {
                        ctx.moveTo(pt.x, pt.y)
                    }
                    
                    ctx.lineTo(pt.x, pt.y)

                    if( pt.mode == "end" || (i == points.length-1) ) {
                        ctx.lineJoin = "round";
                        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;
    }
#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">

我认为您正在寻找 ctx.lineCap property
+ 我修改了你的重绘函数以使用 switch 而不是你令人困惑的 if 语句:

    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">

@Kaiido 正确回答应用 lineCap='round' 将使重绘线的末端变圆。

两个进一步的想法:

  1. 您应该考虑 canvas 相对于文档左上角的偏移位置。否则,如果 canvas 不在文档的左上角,您的 prevX 和 prevY 位置将稍微偏离。

  2. 如果您只允许开始和结束线帽被舍入并且所有中间线帽被对接,您将有一个更清晰的线条(更少 "bulgy")。将 linejoins 保留为默认的斜接。

这是示例代码和演示:

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>