js canvas Implementation of Erase Animation

  • 2021-07-04 17:45:32
  • OfStack

In this paper, we share the implementation principle, implementation code and problems encountered in the implementation process of canvas erase animation for your reference. The specific contents are as follows

The principle is summarized as that one picture is erased and another picture is displayed on the mobile device, which is realized by canvas.
If the user erases manually, it is necessary to monitor touchmove, touchend and other events, calculate the corresponding coordinates, and draw arcs or lines by using clearRect or rect of canvas. However, this will cause the phenomenon of jamming on androd mobile phones.
canvas has an globalCompositeOperation attribute, which defaults to source-over, which is superimposed when you draw on existing pixels. But another attribute is destination-out, That is, display your own target image outside the source image, That is to say, when drawing on the basis of existing pixels, the existing pixels in the area you draw will be set to be transparent. With this attribute, it means that you don't need to use a series of functions such as clip, and you can directly use thick lines or arcs. In this way, the call of api in the drawing environment will be reduced, the performance will be improved, and the operation on android will be much smoother.

Let's show my erase code:


 let requestAnimationFrame = 
  window.requestAnimationFrame || 
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame ||   window.msRequestAnimationFrame;
let cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
let a = 60;

let canvasCleaner = document.getElementById('cas-1');
let ctxCleaner = canvasCleaner.getContext('2d');
let canvasCleanerBox = document.querySelector('.slide-4');
let imgCleaner = new Image();
canvasCleaner.width = canvasCleanerBox.clientWidth * 2;
canvasCleaner.height = canvasCleanerBox.clientHeight * 2;
canvasCleaner.style.width = canvasCleanerBox.clientWidth + 'px';
canvasCleaner.style.height = canvasCleanerBox.clientHeight + 'px';
imgCleaner.src = 'https://gw.alicdn.com/tps/TB1XbyCKVXXXXXEXpXXXXXXXXXX-1080-1920.jpg';
imgCleaner.onload = ()=> {
    let width = parseInt(canvasCleaner.style.width);
    w = canvasCleaner.width*(imgCleaner.height/imgCleaner.width);
    ctxCleaner.drawImage(imgCleaner, 0, 0, canvasCleaner.width, w );

  ctxCleaner.lineCap = 'round';//lineCap  Property sets or returns the style of the line cap at the end of the line. 
  ctxCleaner.lineJoin = 'round';
  ctxCleaner.lineWidth = 100;// Sets or returns the width of the current line 
  ctxCleaner.globalCompositeOperation = 'destination-out';
}

let drawline = (x1, y1,ctx)=> {
  ctx.save();
  ctx.beginPath();
  ctx.arc(x1,y1, a, 0, 2 * Math.PI);
  ctx.fill();//fill()  Method to fill the current image (path). The default color is black. 
  ctx.restore();
};
/* d  In order to erase the coordinates of regional points, I simulated the shapes to be erased and obtained data similar to the following: 
let d2 = [
  [1,190],[30,180],[60,170],[90,168],[120,167],[150,165],[180,164],[210,163],[240,160],[270,159],[300,154],[330,153],[360,152],
  [390,150],[420,140],[450,130],[480,120],[510,120],[540,120],[570,120],[600,120],[630,120],[660,120],[690,120],[720,120],
  [1,190],[20,189],[28,186],[45,185],[50,185],[62,184],[64,182],[90,180],[120,178],
  [160,176],[200,174],[240,172];*/
let draw = (d,ctx)=> {
  if(idx >= d.length) {
    cancelAnimationFrame(ts);
  }else {
    drawline(d[idx][0], d[idx][1],ctx);
    idx++;
    requestAnimationFrame(()=> {
      draw(d, ctx);
    });
  }
}

Because I display the erase animation directly on the page, I don't need the user to erase it himself, so the coordinates of the erase area are calculated by myself. Then use requestAnimationFrame to realize animation, starting with setInterval, and find that setInterval will always be messed up later, so it is recommended not to use setInterval.
In the process of realizing this effect, I found that when I use canvas to drawImage on the page, the picture becomes very blurred?

It turns out that there is an devicePixelRatio attribute in the browser's window variable, which determines how many (usually two) pixels the browser will use to render one pixel. That is to say, assuming that the value of devicePixelRatio is 2, a picture with a size of 100*100 pixels will be rendered with a width of 2 pixels under the retina screen, so the picture will actually occupy a space of 200*200 pixels on the retina screen, which is equivalent to doubling the picture, so the picture becomes blurred.
In this way, the problem about canvas can be easily solved. We can think of canvas as a picture. When the browser renders canvas, it will render canvas with a width of 2 pixels, so the drawn picture or text will be blurred in the browsers of most retina devices.
Similarly, there is an webkitBackingStorePixelRatio attribute in canvas context (safari and chrome only) whose value determines how many pixels the browser will use to store canvas information before rendering canvas. The value in safari under ios6 is 2. Therefore, if a 100*100 picture is drawn in safari, the picture will first generate a 200*200 picture in memory, and then when the browser renders, it will be rendered according to the 100x100 picture, so it becomes 200x200, which is exactly the same as the picture size in memory, so it will not be distorted in safari of iOS. However, there is distortion in safari of chrome and iOS7 because the webkitBackingStorePixelRatio value of safari in chrome and iOS7 is 1.
Solution:


canvas.width = canvasBox.clientWidth * 2;
canvas.height = canvasBox.clientHeight * 2;
canvas.style.width = canvas.clientWidth + 'px';
canvas.style.height = canvas.clientHeight * 'px';
w = canvas.width*(img.height/img.width);
  // console.log(w);
  ctx.drawImage(img, 0, 0, canvas.width , w);

That is, you can create an canvas that is twice the actual size, and then limit the canvas to the actual size with the css style. Or use this polyfill on github, but I tried it and it doesn't seem to work well.


Related articles: