HTML画布绘图和使用透明png基础擦除

时间:2018-03-12 02:45:35

标签: javascript canvas drawing

这是一个jsfiddle https://jsfiddle.net/g10qgefy/46/

if (drawingMode === 'brush') {
      ctx.globalCompositeOperation = "source-atop";

    } else { //erase
      ctx.globalCompositeOperation = "destination-out";
      ctx.strokeStyle = 'rgba(1,0,0,0)';
    }

我正在尝试创建这个简单的绘图功能。我有一个canvas元素,我使用drawImage()绘制一个图像。然后,您可以使用画布方法globalCompositeOperation = "source-atop"刷上非透明像素。

这一切都符合我的要求。但我还需要能够擦除一些绘制的线条而不影响透明图像。所以我只想选择擦除按钮并开始删除我绘制的黑线。

我一直在使用globalCompositionOperation并更改Canvas Rendering | MDN中的值,但这些只会删除所有内容。

我确信有一个解决方案 - 很想听听你的想法!

2 个答案:

答案 0 :(得分:1)

擦除时只需使用原始图像将当前画笔设置为pattern(不重复),或解决大多数警告(缩放,平移等):定义初始画布(绘制图像) in)作为模式并全局存储模式(显示在下面的修改代码中)。

更新虽然这在代码方面是正确的,但事实证明一些浏览器目前在使用canvas元素作为模式源时存在问题(浏览器错误,在本例中是osx上的FF v59,参考。 Kaiido的评论)。

不用担心,但是有一个简单的解决方法:只使用原始图像而不是画布用于模式(如下所示 - 代码示例已更新)。的 ***

这将允许您使用与以前相同的合成模式绘制图像的部分。

// define and set brush
let pattern = ctx.createPattern(img, "no-repeat");
ctx.strokeStyle = pattern;



let canvasElement = document.getElementById("myCanvas");
let img = new Image();
let brushSize = 25;
let brushColor = "#000000"
let drawingMode = 'brush';
let ctx = canvasElement.getContext('2d');
let lastX;
let lastY;
let moving = false;
let pattern;

img.src = 'https://i.pinimg.com/originals/49/af/b1/49afb1d21ae594cb7ac3534a15383711.png';
img.onload = () => {
  ctx.drawImage(img, 0, 0);
  ctx.globalCompositeOperation = "source-atop";
  pattern = ctx.createPattern(img, "no-repeat");
}

let eraseButton = document.getElementById('erase');
let brushButton = document.getElementById('brush');

eraseButton.addEventListener('click', () => {
  drawingMode = 'erase';
  ctx.strokeStyle = pattern; // use pattern for style here
})

brushButton.addEventListener('click', () => {
  drawingMode = 'brush';
  ctx.strokeStyle = "#000";                       // restore current color here
})

canvasElement.addEventListener('mousedown', (ev) => {
  moving = true;
  lastX = ev.pageX;
  lastY = ev.pageY;
})

canvasElement.addEventListener('mouseup', (ev) => {
  moving = false;
  lastX = ev.pageX;
  lastY = ev.pageY;
})

canvasElement.addEventListener('mousemove', (ev) => {
  if (moving) {
    let currentX = ev.pageX;
    let currentY = ev.pageY;

    ctx.beginPath();
    ctx.lineJoin = "round";
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(currentX, currentY);
    ctx.closePath();
    //ctx.strokeStyle = brushColor; // can be set during color selecting (if mode=draw)
    ctx.lineWidth = brushSize;
    ctx.stroke();

    lastX = currentX;
    lastY = currentY;
  }

})

canvas {
  position: absolute;
  left: 0;
  border: 5px solid red;
}

button {
  position: absolute;
  top: 550px;
}

#erase {
  left: 60px;
}

<canvas id="myCanvas" width="500" height="500"></canvas>

<button id="brush">
brush
</button>

<button id="erase">
erase
</button>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

保留两个图层:用户正在绘制的图层和渲染图层。

在第一层(屏幕外画布)上绘制用户的绘图,然后在第二个画布上使用png图像进行合成,即渲染图片。

通过这种方式,您可以安全地执行擦除调用,而无需关心png图层。

&#13;
&#13;
let renderingElement = document.getElementById("myCanvas");
// create an offscreen canvas only for the drawings
let drawingElement = renderingElement.cloneNode();
let drawingCtx = drawingElement.getContext('2d');
let renderingCtx = renderingElement.getContext('2d');


let img = new Image();
let brushSize = 25;
let brushColor = "#000000"
let drawingMode = 'brush';

let lastX;
let lastY;
let moving = false;

img.src = 'https://i.pinimg.com/originals/49/af/b1/49afb1d21ae594cb7ac3534a15383711.png';
img.onload = () => {
  renderingCtx.drawImage(img, 0, 0);
}

let eraseButton = document.getElementById('erase');
let brushButton = document.getElementById('brush');
let exportButton = document.getElementById('export');

eraseButton.addEventListener('click', () => {
  drawingMode = 'erase';
})

brushButton.addEventListener('click', () => {
  drawingMode = 'brush';
})

renderingElement.addEventListener('mousedown', (ev) => {
  moving = true;
  lastX = ev.pageX - renderingElement.offsetLeft;
  lastY = ev.pageY - renderingElement.offsetTop;
})

renderingElement.addEventListener('mouseup', (ev) => {
  moving = false;
  lastX = ev.pageX - renderingElement.offsetLeft;
  lastY = ev.pageY - renderingElement.offsetTop;
})

renderingElement.addEventListener('mousemove', (ev) => {
  if (moving) {
    if (drawingMode === 'brush') {
      drawingCtx.globalCompositeOperation = "source-over";
    } else {
      drawingCtx.globalCompositeOperation = "destination-out";
    }
    let currentX = ev.pageX - renderingElement.offsetLeft;
    let currentY = ev.pageY - renderingElement.offsetTop;

    drawingCtx.beginPath();
    drawingCtx.lineJoin = "round";
    drawingCtx.moveTo(lastX, lastY);
    drawingCtx.lineTo(currentX, currentY);
    drawingCtx.closePath();
    drawingCtx.strokeStyle = brushColor;
    drawingCtx.lineWidth = brushSize;
    drawingCtx.stroke();

    lastX = currentX;
    lastY = currentY;

    // draw to visible canvas
    renderingCtx.clearRect(0, 0, renderingElement.width, renderingElement.height);
    renderingCtx.drawImage(img, 0, 0);
    renderingCtx.globalCompositeOperation = 'source-atop';
    renderingCtx.drawImage(drawingElement, 0, 0);
    
    // reset
    renderingCtx.globalCompositeOperation = 'source-over';
  }

});
&#13;
canvas {
  border: 5px solid red;
}
&#13;
<button id="brush">brush</button>
<button id="erase">erase</button>
<canvas id="myCanvas" width="500" height="500"></canvas>
&#13;
&#13;
&#13;