如何在不清除画布的情况下在画布上绘制平滑的线条?

时间:2019-09-16 11:42:51

标签: javascript canvas

我有一个画布,该画布在加载时动态添加到页面中。 我想在画布上绘制用户的鼠标路径,但是我发现,如果在绘制之前清除画布,它将绘制平滑的线条,否则,它将绘制以下屏幕截图所示的难看的线条! enter image description here

要测试该问题,请在代码中取消注释draw_on_canvas函数的第一行以查看区别。

$(document).ready(function() {
  //Create DRAWING environment
  var canvasWidth = 400;
  var canvasHeight = 200;
  var drawn_shape_list = [];
  var current_shape_info = {};
  var is_painting = false;

  function add_path_to_drawn_shape_list() {
    if (current_shape_info.path && current_shape_info.path.length > 0) {
      drawn_shape_list.push(current_shape_info);
    }

    current_shape_info = {};
  }

  function add_path(x, y) {
    current_shape_info.color = "#000000";
    current_shape_info.size = 2;

    if (!current_shape_info.path) {
      current_shape_info.path = [];
    }

    current_shape_info.path.push({
      "x": x,
      "y": y
    });
  }

  function draw_on_canvas() {
    //Uncomment following line to have smooth drawing!
    //context.clearRect(0, 0, context.canvas.width, context.canvas.height); //clear canvas
    context.strokeStyle = current_shape_info.color;
    context.lineWidth = current_shape_info.size;

    context.beginPath();
    context.moveTo(current_shape_info.path[0].x, current_shape_info.path[0].y);

    for (var i = 1; i < current_shape_info.path.length; i++) {
      context.lineTo(current_shape_info.path[i].x, current_shape_info.path[i].y);
    }
    context.stroke();
  }

  //Create canvas node
  var canvas_holder = document.getElementById('canvas_holder');
  canvas = document.createElement('canvas');
  canvas.setAttribute('width', canvasWidth);
  canvas.setAttribute('height', canvasHeight);
  canvas.setAttribute('id', 'whitboard_canvas');
  canvas_holder.appendChild(canvas);
  if (typeof G_vmlCanvasManager != 'undefined') {
    canvas = G_vmlCanvasManager.initElement(canvas);
  }
  context = canvas.getContext("2d");

  $('#canvas_holder').mousedown(function(e) {
    var mouseX = e.pageX - this.offsetLeft;
    var mouseY = e.pageY - this.offsetTop;

    is_painting = true;
    add_path(mouseX, mouseY, false);
    draw_on_canvas();
  });

  $('#canvas_holder').mousemove(function(e) {
    if (is_painting) {
      var mouseX = e.pageX - this.offsetLeft;
      var mouseY = e.pageY - this.offsetTop;

      var can = $('#whitboard_canvas');
      add_path(mouseX, mouseY, true);
      draw_on_canvas();
    }
  });

  $('#canvas_holder').mouseup(function(e) {
    is_painting = false;
    add_path_to_drawn_shape_list();
  });

  $('#canvas_holder').mouseleave(function(e) {
    is_painting = false;
    add_path_to_drawn_shape_list();
  });
});
#canvas_holder {
  border: solid 1px #eee;
}

canvas {
  border: solid 1px #ccc;
}
<HTML>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <div id="canvas_holder"></div>
</body>

</HTML>

您可以看到我的带有两个画布的代码here的示例。

我尝试过context.lineJoin = "round";context.lineCap = 'round';,但结果没有改变。

这是正常的画布行为还是应该设置一些内容?

1 个答案:

答案 0 :(得分:0)

  

如何在不清除画布的情况下在画布上绘制平滑线?

你不知道。清除并重新绘制是必经之路。

  

这是正常的画布行为

完全可以。除非您执行一些应该清除画布的操作,否则不会清除画布。因此,当您使用半透明颜色在同一区域上绘制多次时,像素将变得越来越暗。

不要担心性能,不得不处理以前的图形的合成甚至比绘制一条更复杂的路径还要慢。

可以提高性能的一件事是使用单个Path,这样在每一帧上都只发生一次绘制操作:

const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const path = new Path2D();
const mouse = {};

function draw() {
  // clear all
  ctx.clearRect( 0, 0, canvas.width, canvas.height );
  // draw the single path
  ctx.stroke( path );
  // tell we need to redraw next frame
  mouse.dirty = false;
}

canvas.onmousedown = (evt) => {
  mouse.down = true;
  // always use the same path
  path.moveTo( evt.offsetX, evt.offsetY );
};
document.onmouseup = (evt) => {
  mouse.down = false;
};
document.onmousemove = (evt) => {
  if( mouse.down ) {
    const rect = canvas.getBoundingClientRect();
    path.lineTo( evt.clientX - rect.left, evt.clientY - rect.top );
  }
  if( !mouse.dirty ) {
    mouse.dirty = true;
    requestAnimationFrame(draw);
  }
};
canvas { border: 1px solid }
<canvas id="canvas" width="500" height="500"></canvas>

如果需要使用不同的路径样式,则可以为每种样式创建一个路径。

const canvas = document.getElementById( 'canvas' );
const ctx = canvas.getContext( '2d' );
const makePath = (color) => ({
  color,
  path2d: new Path2D()
});
const pathes = [makePath('black')];

const mouse = {};

function draw() {
  // clear all
  ctx.clearRect( 0, 0, canvas.width, canvas.height );
  pathes.forEach( (path) => {
    // draw the single path
    ctx.strokeStyle = path.color;
    ctx.stroke( path.path2d );
  } );
  // tell we need to redraw next frame
  mouse.dirty = false;
}

document.getElementById('inp').onchange = (evt) =>
  pathes.push( makePath( evt.target.value ) );

canvas.onmousedown = (evt) => {
  mouse.down = true;
  const path = pathes[ pathes.length - 1 ].path2d;
  // always use the same path
  path.moveTo( evt.offsetX, evt.offsetY );
};
document.onmouseup = (evt) => {
  mouse.down = false;
};
document.onmousemove = (evt) => {
  if( mouse.down ) {
    const rect = canvas.getBoundingClientRect();
    const path = pathes[ pathes.length - 1 ].path2d;
    path.lineTo( evt.clientX - rect.left, evt.clientY - rect.top );
  }
  if( !mouse.dirty ) {
    mouse.dirty = true;
    requestAnimationFrame(draw);
  }
};
canvas { border: 1px solid }
<input type="color" id="inp"><br>
<canvas id="canvas" width="500" height="500"></canvas>

相关问题