擦除 HTML5 Canvas 上的部分图像?

Erasing Parts of an Image on HTML5 Canvas?

我有一个 HTML5 Canvas。我正在使用 KineticJS(KonvaJS) canvas 库。在一片空白 canvas 上,我绘制了一个图像,如下图所示。现在我想创建一个圆形形状,可用于擦除部分图像。图中红圈就是橡皮擦

如何删除 HTML5 Canvas 上的部分图像?

简而言之JavaScript这很简单。

首先准备好 canvas 和绘图上下文:

var context=document.getElementById("your_canvas_id").getContext("2d");
var image=document.getElementById("your_image_id");

现在您想将图像绘制到上下文中:

context.drawImage(image,0,0,image.width,image.height,0,0,image.width,image.height);

现在,当您想擦除部分图像时,只需在 canvas 上画画即可:

var x=y=radius=10;// Circle coordinates and radius.

context.fillStyle="#ffffff";// Your eraser color (not transparent)
context.beginPath();
context.arc(x,y,radius,0,Math.PI*2);
context.fill();

然而,这只是模拟擦除。如果你希望你擦除的东西之后是透明的,你可以查看 context.clearRect,但我不确定你如何用一个圆圈来做到这一点。

您可以对 "erase" 像素使用合成。

具体来说,您使用 destination-out 合成。

KineticJS 不支持合成,但您还有几个选择:

(注意:KineticJS已经变成了KonvaJS,我还没有检查KonvaJs是否支持合成。如果现在支持,就在KonvaJS中使用destination-out合成)

选项#1:使用原生 canvas 元素作为您的 Kinetic.Image 来源

  • 使用var c=document.createElement,

  • 在内存中创建html5canvas
  • 将 canvas 调整为图像大小,

  • drawImage 你的图片放到 canvas,

  • 创建一个 Kinetic.Image 并将其图像 属性 设置为对本机 canvas 的引用。 Kinetic.Image 将显示绘制到原生 canvas.

    上的任何内容
    var kImage=new Kinetic.Image({
    ...
    image:c,
    ...
    
  • 设置 canvas 合成,使新绘图成为 "erase" 现有像素:

    c.globalCompositeOperation='destination-out';
    
  • 监听圆圈橡皮擦上的拖动事件。使用这些事件在 canvas 上绘制一个圆圈,该圆圈的移动就像 Kinetic 圆圈橡皮擦的移动一样。由于 canvas 的合成设置为 "erase",因此在 canvas 上绘制的新圆将擦除 canvas 上的图像。

您的 Kinetic.Image 准确地反映了其 canvas 来源 (var c),因此您的 Kinetic.Image 也将显示正在擦除的图像以响应 Kinetic 圆圈橡皮擦运动。

选项 #2:使用 Kinetic.Shape

您可以通过在单独的图层上创建 Kinetic.Shape 并使用以下方法获取对本机 canvas 上下文的引用来执行与选项 #1 相同的操作:

var ctx = myShapeLayer.getContext()._context;

这是一个较弱的选项,因为 KineticJS 将重新绘制形状——导致您的擦除被撤消。因此,您必须执行额外的步骤,保存所有圆圈橡皮擦的动作并重播这些动作(在 drawFunc 中)以重做擦除。

感谢markE的详细解答,

我已经尝试从 Konva.Layer() 获取上下文并且成功了。

    var freeHandDrawingImage = new Image();
    freeHandDrawingImage.onload = function() {
        var context = freeHandDrawingLayer.getContext('2d');
        context.drawImage(this, 0,0);
        context.globalCompositeOperation='destination-out';
        freeHandDrawingLayer.draw();
    };
    freeHandDrawingImage.src = "image.png";

并且我已经使用 Konva.Shape 擦除 "destination-out" 并通过自定义 "source-over":

自由绘制
freeDrawingType = 'brush';
isFreeDrawingMode = false;
isPaint = false;
lastPointerPosition = {};

drawFreeDrawings = function(){

    var freeDraw = new Konva.Shape({
        name: "freeDraw",
        stroke: 'black',
        strokeWidth: 5,
        closed : false,
        sceneFunc: function(context){
            // free draw quad
            debugger;
            if(isPaint){
                if (freeDrawingType === 'brush') {
                    context.globalCompositeOperation = 'source-over';
                }
                if (freeDrawingType === 'eraser') {
                    context.globalCompositeOperation = 'destination-out';
                }
                context.beginPath();
                context.moveTo(lastPointerPosition.x, lastPointerPosition.y);
                var newPosition = stage.getPointerPosition();

                context.lineTo(newPosition.x, newPosition.y);
                context.stroke();
                debugger;
                lastPointerPosition = newPosition;
                context.strokeShape(this);

            }
        }
    });
    freeHandDrawingLayer.add(freeDraw);
    // now we need to bind some events
    // we need to start drawing on mousedown
    // and stop drawing on mouseup
    selectionBoxBackground.on('mousedown', function() {
        if(isFreeDrawingMode){
            isPaint = true;
            lastPointerPosition = stage.getPointerPosition();
            stage.draw();
        }
    });

    selectionBoxBackground.on('mouseup', function() {
        if(isFreeDrawingMode){
            isPaint = false;
        }
    });

    // and core function - drawing
    selectionBoxBackground.on('mousemove', function() {
            if (!isPaint) {
                return;
            }      
            freeHandDrawingLayer.draw();
    });
}