这是一个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中的值,但这些只会删除所有内容。
我确信有一个解决方案 - 很想听听你的想法!
答案 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;
答案 1 :(得分:0)
保留两个图层:用户正在绘制的图层和渲染图层。
在第一层(屏幕外画布)上绘制用户的绘图,然后在第二个画布上使用png图像进行合成,即渲染图片。
通过这种方式,您可以安全地执行擦除调用,而无需关心png图层。
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;