简体   繁体   English

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

[英]How to crop canvas image by path, not area

Im making a painting tool, and one of the feature is showing cropped image of the drawn path. 我正在制作绘画工具,其中一项功能是显示绘制路径的裁剪图像。

The path I have drawn(image) 我绘制的路径(图片)

For example in above the picture, the white colored path indicates what I have drawn, just like a painting tool. 例如,在图片上方,白色的路径表示我已绘制的内容,就像绘制工具一样。

Cropped image 裁剪图像

And here is the cropped image of the path. 这是路径的裁剪图像。 If you look at the picture, you can see that it crops the image as if the path is closed and therefore it crops the image "area" not the path. 如果查看图片,您会看到它像关闭路径一样裁剪图像,因此它裁剪了图像“区域”而不是路径。

and here is the code 这是代码

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
}

The crop function receives points which is consisted [x coordinate, y coordinate][ ] of the drawn path. crop功能接收points ,其由[x坐标,y坐标]所绘制路径的[]。

Is there an way to show image only the path that I've painted? 有没有办法只显示我绘制的路径的图像?

That's more what is generally called a mask then, but note that both for the current clip or for the mask you want to attain, the best is to use compositing. 那就是通常所说的遮罩,但是请注意,对于当前剪辑或要获得的遮罩,最好是使用合成。

Canvas context has various compositing options , allowing you to generate complex compositions, from pixels's alpha value. 画布上下文具有多种合成选项 ,可让您根据像素的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> 

Don't hesitate to look at all the compositing options, various cases will require different options, for instance if you need to draw multiple paths, you may prefer to render first your paths and then keep your image only where you did already drawn, using the source-atop option: 不要犹豫,查看所有合成选项,各种情况下将需要不同的选项,例如,如果您需要绘制多个路径,则可能更喜欢先渲染路径,然后仅将图像保留在已绘制的位置,使用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> 

And also remember that you can very well have canvases that you won't append to the document that you can use as layers to generate really complex compositions. 还要记住,您可以很好地拥有不会添加到文档中的画布,这些画布可以用作图层来生成真正复杂的合成。 ( drawImage() does accept a <canvas> as source). drawImage()确实接受<canvas>作为源)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM