CaraGee CaraGee - 6 months ago 17
jQuery Question

How to clear only the animated section of the canvas between loops

The animation linked in this JSFiddle should show an EKG style machine, with a set of lines scrolling across the screen, which repeats on a loop after reverting briefly to a blank screen.

It's a modification of some great HTML5 Canvas a clipping animation sample code (http://tajvirani.com/2012/03/30/html5-canvas-animating-a-clip-mask-path/)

First loop works a charm but there are some flickering artefacts from then on.

How do I to 'revert' to the original look (machine image with a blank, black screen) before each iteration?

I thought c1.clearRect(0, 0, 386, 380); - line 42 of the JSFiddle - did that, but no joy.

setInterval(animateManaging,6000);

function animateManaging() {
// Grabs the canvas element we made above
var ca1=document.getElementById("cvs3");

// Defines the 2d thing, standard for making a canvas
var c1=ca1.getContext("2d");

// Creates an image variable to hold and preload our image (can't do animations on an image unless its fully loaded)
var img1 = document.createElement('IMG');

// Loads image link into the img element we created above
img1.src = "http://s33.postimg.org/3pk551xcv/managing_values.png";

// Creates the first save event, this gives us a base to clear our clipping / mask to since you can't just delete elements.
c1.save();

// Our function for when the image loads
img1.onload = function () {
// clear off the canvas


// First call to our canvas drawing function, the thing that is going to do all the work for us.
drawc1r(0);

// The function that is doing all the heavy lifting. The reason we are doing a function is because
// to make an animation we have to draw the circle (or element) frame by frame, to do this manually would be to time
// intensive so we are just going to create a loop to do it. 'i' stands for the radius of our border
// so over time our radius is going to get bigger and bigger.
function drawc1r(i) {
// Creates a save state. Imagine a save state like an array, when you clear one it pops last element in the array off the stack
// When you save, it creates an element at the top of the stack. So if we cleared without making new ones, we would end up with nothing on our stage.

c1.save();

// This clears everything off the stage, I do this because our image has transparency, and restore() (the thing that pops one off the stack)
// Doesn't clear off images, and so if we stick an image on the stage over and over, the transparency will stack on top of each other and
// That isn't quite what we want.

c1.clearRect(0, 0, 386, 380);

// Adds one to the interval count
i++;

// Tells canvas we are going to start creating an item on the stage - it can be a line, a rectangle or any shape you draw, but whatever
// after this path will be added to the clip when its called. I can have 3 rectangles drawn and that would make a clip.
c1.beginPath();

// make the clipping rectangle, using i to make it grow on each interval
c1.rect(0,0,i*13,380);

// After everything is defined, we make a clip area out of it.
c1.clip();

// Now that we have the clip added to it, we are going to add the image to the clip area.
c1.drawImage(img1, 0, 0);

// This pops one off the stack which gets rid of the clip so we can enlarge it and make it again on the next pass
c1.restore();

// Here is the final size of the rectangle, I want it to grow until it hits 380 so we set a timeout to run this function again
// until we get the size we want. The time in milliseconds pretty much defines your framerate


if (i < 380) {
setTimeout(function () {
drawc1r(i);
}, 100);
}
}
}
}


https://jsfiddle.net/0o1x1ryp/#&togetherjs=nftC29hATf

Answer

Biggest issue here is that you are calling animateManaging every six seconds, so you are creating many 'instances' of a drawer that will draw at the same time and create that flickering.

Rather than that, just call animateManaging once, and handle the loop yourself : what you want is, when right side is reached, to clear things and to restart on the left : remove your clearRect and change your last if to :

  if (i * moveSpeed < viewWidth) {
    // just resume if right side not reach
    setTimeout(function() {
      drawc1r(i);
    }, animInterval);
  } else {
    // clear and restart from 0 when right side reached.
    setTimeout(function() {
      c1.clearRect(0, 0, viewWidth, viewHeight);
      drawc1r(0);
    }, animInterval);
  }

Besides that, i added some vars to avoid hard coded constants ( moveSpeed, viewWidth, viewHeight, animInterval), and i set the source of the image after setting the onload handler.

Following fiddle should be close to your needs :

https://jsfiddle.net/gamealchemist/0o1x1ryp/5/