画布lineTo中更平滑的lineWidth更改

时间:2014-10-03 00:24:46

标签: javascript html5 canvas line

所以我试图在HTML5画布中创建一个绘图工具,其中笔划的重量越大,移动鼠标的速度越快,移动速度越慢。我使用ctx.lineTo(),但是在我的第一次尝试中注意到,如果我移动得太快,厚度的变化会记录为明显的方形增量(而不是平滑增加的重量)

first try

所以我将ctx.lineJoin和ctx.lineCap更改为" round"它有点好转

current state

但这仍然不像我想的那么顺利。我正在为这样的事情拍摄

what i'd like

关于如何使体重变化更平滑的任何建议都会很棒!这是一个有效的演示:http://jsfiddle.net/0fhag522/1/

这里'预览我的" dot"对象(笔)和我的绘图功能:

    var dot = {         
        start: false,
        weight: 1,
        open: function(x,y){
            ctx.lineJoin = "round";
            ctx.lineCap = "round";
            ctx.beginPath();
            ctx.moveTo(x,y);
        },
        connect: function(x,y){
            ctx.lineWidth = this.weight;
            ctx.lineTo(x,y);
            ctx.stroke();
            ctx.closePath();
            ctx.beginPath();
            ctx.moveTo(x,y);
        },
        close: function(){
            ctx.closePath();
        }
    }

    function draw(){
        if(down){
            if(!dot.start){ 
                dot.close();
                prevx = mx;  prevy = my;
                dot.open(mx,my); 
                dot.start=true;  
            }
            else { 
                var dx = (prevx>mx) ? prevx-mx : mx-prevx;
                var dy = (prevy>my) ? prevy-my : my-prevy;
                dot.weight = Math.abs(dx-dy)/2;
                dot.connect( mx,my );
                prevx = mx;  prevy = my;
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

由于画布没有可变宽度线,因此必须在线点之间绘制闭合路径。

然而,这留下了明显的对接。

enter image description here

要平滑对接关节,可以在每个关节处画一个圆圈。

enter image description here

以下是示例代码和演示:

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 scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();

var isDown = false;
var startX;
var startY;

var PI = Math.PI;
var halfPI = PI / 2;
var points = [];

$("#canvas").mousedown(function(e) {
  handleMouseDown(e);
});

function handleMouseDown(e) {
  e.preventDefault();
  e.stopPropagation();

  mx = parseInt(e.clientX - offsetX);
  my = parseInt(e.clientY - offsetY);

  var pointsLength = points.length;

  if (pointsLength == 0) {
    points.push({
      x: mx,
      y: my,
      width: Math.random() * 5 + 2
    });
  } else {
    var p0 = points[pointsLength - 1];
    var p1 = {
      x: mx,
      y: my,
      width: Math.random() * 5 + 2
    };
    addAngle(p0, p1);
    p0.angle = p1.angle;
    addEndcap(p0);
    addEndcap(p1);
    points.push(p1);
    extendLine(p0, p1);
  }
}

function addAngle(p0, p1) {
  var dx = p1.x - p0.x;
  var dy = p1.y - p0.y;
  p1.angle = Math.atan2(dy, dx);
}

function addEndcap(p) {
  p.x0 = p.x + p.width * Math.cos(p.angle - halfPI);
  p.y0 = p.y + p.width * Math.sin(p.angle - halfPI);
  p.x1 = p.x + p.width * Math.cos(p.angle + halfPI);
  p.y1 = p.y + p.width * Math.sin(p.angle + halfPI);
}

function extendLine(p0, p1) {
  ctx.beginPath();
  ctx.moveTo(p0.x0, p0.y0);
  ctx.lineTo(p0.x1, p0.y1);
  ctx.lineTo(p1.x1, p1.y1);
  ctx.lineTo(p1.x0, p1.y0);
  ctx.closePath();
  ctx.fillStyle = 'blue';
  ctx.fill();
  // draw a circle to cover the butt-joint
  ctx.beginPath();
  ctx.moveTo(p1.x, p1.y);
  ctx.arc(p1.x, p1.y, p1.width, 0, Math.PI * 2);
  ctx.closePath();
  ctx.fill();
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click to add line segments.</h4>
<canvas id="canvas" width=300 height=300></canvas>

答案 1 :(得分:0)

这是创建具有圆线上限生长线的简单功能:

/* 
 * this function returns a Path2D object
 * the path represents a growing line between two given points
 */
function createGrowingLine (x1, y1, x2, y2, startWidth, endWidth) {
  // calculate direction vector of point 1 and 2
  const directionVectorX = x2 - x1,
        directionVectorY = y2 - y1;
  // calculate angle of perpendicular vector
  const perpendicularVectorAngle = Math.atan2(directionVectorY, directionVectorX) + Math.PI/2;
  // construct shape
  const path = new Path2D();
  path.arc(x1, y1, startWidth/2, perpendicularVectorAngle, perpendicularVectorAngle + Math.PI);
  path.arc(x2, y2, endWidth/2, perpendicularVectorAngle + Math.PI, perpendicularVectorAngle);
  path.closePath();
  return path;
}

const ctx = myCanvas.getContext('2d');
// create a growing line between P1(10, 10) and P2(250, 100)
// with a start line width of 10 and an end line width of 50
let line1 = createGrowingLine(10, 10, 250, 100, 10, 50);
ctx.fillStyle = 'green';
// draw growing line
ctx.fill(line1);
<canvas width="300" height="150" id="myCanvas"></canvas>

说明: 函数createGrowingLine通过以下方式在两个给定点之间构造形状:

  1. 计算两个点的方向向量
  2. 计算垂直向量的弧度角
  3. 创建一个以计算的角度到计算的角度+ 180度为起点和半径的半圆路径
  4. 从计算的角度+ 180度到计算的角度以及终点的中心和半径创建另一个半圆路径
  5. 通过连接第一个圆的起点和第二个圆的终点来闭合路径

如果您不想使用圆角线帽,请使用以下功能:

/* 
 * this function returns a Path2D object
 * the path represents a growing line between two given points
 */
function createGrowingLine (x1, y1, x2, y2, startWidth, endWidth) {
  const startRadius = startWidth/2;
  const endRadius = endWidth/2;
  // calculate direction vector of point 1 and 2
  let directionVectorX = x2 - x1,
      directionVectorY = y2 - y1;
  // calculate vector length
  const directionVectorLength = Math.hypot(directionVectorX, directionVectorY);
  // normalize direction vector (and therefore also the perpendicular vector)
  directionVectorX = 1/directionVectorLength * directionVectorX;
  directionVectorY = 1/directionVectorLength * directionVectorY;
  // construct perpendicular vector
  const perpendicularVectorX = -directionVectorY,
        perpendicularVectorY = directionVectorX;
  // construct shape
  const path = new Path2D();
  path.moveTo(x1 + perpendicularVectorX * startRadius, y1 + perpendicularVectorY * startRadius);
  path.lineTo(x1 - perpendicularVectorX * startRadius, y1 - perpendicularVectorY * startRadius);
  path.lineTo(x2 - perpendicularVectorX * endRadius, y2 - perpendicularVectorY * endRadius);
  path.lineTo(x2 + perpendicularVectorX * endRadius, y2 + perpendicularVectorY * endRadius);
  path.closePath();
  return path;
}

const ctx = myCanvas.getContext('2d');
// create a growing line between P1(10, 10) and P2(250, 100)
// with a start line width of 10 and an end line width of 50
let line1 = createGrowingLine(10, 10, 250, 100, 10, 50);
ctx.fillStyle = 'green';
// draw growing line
ctx.fill(line1);
<canvas width="300" height="150" id="myCanvas"></canvas>