如何按路径而不是区域裁剪画布图像

时间:2019-04-02 02:00:13

标签: javascript svg canvas

我正在制作绘画工具,其中一项功能是显示绘制路径的裁剪图像。

The path I have drawn(image)

例如,在图片上方,白色的路径表示我已绘制的内容,就像绘画工具一样。

Cropped image

这是路径的裁剪图像。如果您查看图片,可以看到它像关闭路径一样裁剪图像,因此可以裁剪图像“区域”而不是路径。

这是代码

function crop({ image, points }) {
  return Observable.create(observer => {
    const { width, height } = getImageSize(image);
    const canvas = document.createElement('canvas') as HTMLCanvasElement;
    const context = canvas.getContext('2d');
    canvas.width = width;
    canvas.height = height;

    context.beginPath();
    points.forEach(([x, y], idx) => {
      if (idx === 0) {
        context.moveTo(x, y);
      } else {
        context.lineTo(x, y);
      }
    });
    context.clip();

    context.drawImage(image);

    ...etc
}

crop函数接收points,它由绘制路径的[x坐标,y坐标] []组成。

有没有办法只显示我绘制的路径的图像?

1 个答案:

答案 0 :(得分:2)

那通常就是所谓的遮罩,但是请注意,对于当前剪辑或要获得的遮罩,最好是使用合成。

画布上下文具有各种compositing options,可让您根据像素的alpha值生成复杂的构图。

const ctx = canvas.getContext('2d');
const pathes = [[]];
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_%2826908986301%29.jpg/320px-Serene_Sunset_%2826908986301%29.jpg';

function begin() {
  canvas.width = this.width;
  canvas.height = this.height;
  ctx.lineWidth = 10;
  addEventListener('mousemove', onmousemove);
  addEventListener('mousedown', onmousedown);
  addEventListener('mouseup', onmouseup);
  anim();
  ctx.fillText("Use your mouse to draw a path", 20,50) 
}

function anim() {
  requestAnimationFrame(anim);
  if(dirty) draw();
  dirty = false;
}

function draw() {
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.drawImage(bg, 0, 0);
  ctx.beginPath();
  pathes.forEach(path => {
    if(!path.length) return;
    ctx.moveTo(path[0].x, path[0].y);
    path.forEach(pt => {
      ctx.lineTo(pt.x, pt.y);
    });
  });
  // old drawings will remain on where new drawings will be
  ctx.globalCompositeOperation = 'destination-in';
  ctx.stroke();
  // reset
  ctx.globalCompositeOperation = 'source-over';
}

function onmousemove(evt) {
  if(!down) return;
  const rect = canvas.getBoundingClientRect();
  pathes[pathes.length - 1].push({
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  });
  dirty = true;
}
function onmousedown(evt) {
  down = true;
}
function onmouseup(evt) {
  down = false;
  pathes.push([]);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>

不要犹豫,查看所有合成选项,各种情况下将需要不同的选项,例如,如果需要绘制多个路径,则可能更喜欢先渲染路径,然后仅将图像保留在已经完成的位置使用source-atop选项绘制:

const ctx = canvas.getContext('2d');
const pathes = [[]];
pathes[0].lineWidth = (Math.random() * 20) + 0.2;
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_%2826908986301%29.jpg/320px-Serene_Sunset_%2826908986301%29.jpg';

function begin() {
  canvas.width = this.width;
  canvas.height = this.height;
  addEventListener('mousemove', onmousemove);
  addEventListener('mousedown', onmousedown);
  addEventListener('mouseup', onmouseup);
  anim();
  ctx.fillText("Use your mouse to draw a path", 20,50) 
}

function anim() {
  requestAnimationFrame(anim);
  if(dirty) draw();
  dirty = false;
}

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

  pathes.forEach(path => {
    if(!path.length) return;
    ctx.beginPath();
    ctx.lineWidth = path.lineWidth;
    ctx.moveTo(path[0].x, path[0].y);
    path.forEach(pt => {
      ctx.lineTo(pt.x, pt.y);
    });
    ctx.stroke();
  });
  // new drawings will appear on where old drawings were
  ctx.globalCompositeOperation = 'source-atop';
  ctx.drawImage(bg, 0, 0);
  
  // reset
  ctx.globalCompositeOperation = 'source-over';
}

function onmousemove(evt) {
  if(!down) return;
  const rect = canvas.getBoundingClientRect();
  pathes[pathes.length - 1].push({
    x: evt.clientX - rect.left,
    y: evt.clientY - rect.top
  });
  dirty = true;
}
function onmousedown(evt) {
  down = true;
}
function onmouseup(evt) {
  down = false;
  const path = [];
  path.lineWidth = (Math.random() * 18) + 2;
  pathes.push(path);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>

并且还请记住,您可以很好地拥有不会附加到文档的画布,这些画布可以用作图层来生成真正复杂的合成。 (drawImage()确实接受作为源)。