在JS中仅擦除画布的拼接元素

时间:2018-08-05 10:18:53

标签: javascript html canvas

我想创建类似刮刮卡的东西。 我创建了一个画布并向其中添加了文本。我在文本上方添加了一个框来隐藏它。最后写下代码以擦除(刮擦)该框。

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50); 
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle='red';
ctx.fillRect(0,0,500,500);
function myFunction(event) {
  var x = event.touches[0].clientX;
  var y = event.touches[0].clientY;
  document.getElementById("demo").innerHTML = x + ", " + y;
  ctx.globalCompositeOperation = 'destination-out';
  ctx.arc(x,y,30,0,2*Math.PI);
  ctx.fill();
}

但是问题在于它也删除了文本。
如何只删除该框而不删除文本?

2 个答案:

答案 0 :(得分:1)

  

我如何只删除该框而不删除文本?

您不能,必须重绘文本。一旦在文本上方绘制了框,就将其抹掉了,它不再存在。画布是基于像素的,而不是像SVG一样基于形状的。

答案 1 :(得分:1)

画布上下文仅保留一种绘制状态,即一种绘制状态。如果修改像素,它将不记得它以前的样子,并且由于它没有内置的图层概念,因此当您清除像素时,它只是一个透明像素。

因此,要实现您想要的目标,最简单的方法就是自己构建分层逻辑,例如,通过创建两个“ 屏幕外”画布,如“未附加在DOM中”,其中一个用于可刮擦的区域,以及应该显示的背景区域。

然后在第三个画布上,每次都绘制两个画布。这是将向您的用户展示的第三张画布:

var canvas = document.getElementById("myCanvas");
// the context that will be presented to the user
var main = canvas.getContext("2d"); 
// an offscreen one that will hold the background
var background = canvas.cloneNode().getContext("2d");
// and the one we will scratch
var scratch = canvas.cloneNode().getContext("2d");
generateBackground();
generateScratch();

drawAll();

// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;

function drawAll() {
  main.clearRect(0,0,canvas.width,canvas.height);
  main.drawImage(background.canvas, 0,0);
  main.drawImage(scratch.canvas, 0,0);
}

function generateBackground(){
  background.font = "30px Arial";
  background.fillText("Hello World",10,50);
}
function generateScratch() {
  scratch.fillStyle='red';
  scratch.fillRect(0,0,500,500);
  scratch.globalCompositeOperation = 'destination-out';
}

function handlemousedown(evt) {
  down = true;
  handlemousemove(evt);
}
function handlemouseup(evt) {
  down = false;
}
function handlemousemove(evt) {
  if(!down) return;
  var x = evt.clientX - canvas.offsetLeft;
  var y = evt.clientY - canvas.offsetTop;
  scratch.beginPath();
  scratch.arc(x, y, 30, 0, 2*Math.PI);
  scratch.fill();
  drawAll();
}
<canvas id="myCanvas"></canvas>

现在,所有这些都可以在同一画布上完成,但是从性能角度来看,这可能不是最好的,因为这意味着生成了一个过于复杂的子路径,应该在每次绘制时重新渲染它,这是实施起来并不容易:

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext('2d');
ctx.font = '30px Arial';
drawAll();

// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;

function drawAll() {
  ctx.globalCompositeOperation = 'source-over';
  // first draw the scratch pad, intact
  ctx.fillStyle = 'red';
  ctx.fillRect(0,0,500,500);
  // then erase with the currently being defined path
  // see 'handlemousemove's note
  ctx.globalCompositeOperation = 'destination-out';
  ctx.fill();
  // finally draw the text behind
  ctx.globalCompositeOperation = 'destination-over';
  ctx.fillStyle = 'black';
  ctx.fillText("Hello World",10,50);
}

function handlemousedown(evt) {
  down = true;
  handlemousemove(evt);
}
function handlemouseup(evt) {
  down = false;
}
function handlemousemove(evt) {
  if(!down) return;
  var x = evt.clientX - canvas.offsetLeft;
  var y = evt.clientY - canvas.offsetTop;
  // note how here we don't create a new Path,
  // meaning that all the arcs are being added to the single one being rendered
  ctx.moveTo(x, y);
  ctx.arc(x, y, 30, 0, 2*Math.PI);
  drawAll();
}
<canvas id="myCanvas"></canvas>