简体   繁体   中英

Erasing only paticular element of canvas in JS

I want to create something like scratch card. I created a canvas and added text to it.I than added a box over the text to hide it.Finally write down the code to erase(scratch) that box.

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();
}

But the problem is it delete the text also.
How could I only delete that box not the text?

How could I only delete that box not the text?

You can't, you'll have to redraw the text. Once you've drawn the box over the text, you've obliterated it, it doesn't exist anymore. Canvas is pixel-based, not shape-based like SVG.

Canvas context keeps only one drawing state, which is the one rendered. If you modify a pixel, it won't remember how it was before, and since it has no built-in concept of layers, when you clear a pixel, it's just a transparent pixel.

So to achieve what you want, the easiest is to build this layering logic yourself, eg by creating two " off-screen " canvases, as in "not appended in the DOM", one for the scratchable area, and one for the background that should be revealed.

Then on a third canvas, you'll draw both canvases every time. It is this third canvas that will be presented to your user:

 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> 

Now, it could all have been done on the same canvas, but performance wise, it's probably not the best, since it implies generating an overly complex sub-path that should get re-rendered at every draw, also, it is not much easier to implement:

 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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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