javascripthtmlanimationcanvasglobalcompositeoperation

Canvas disappears when animating "source-in" globalCompositeOperation mask


I'm using "source-in" compositing gradient mask on a canvas and I want to animate the stretching of the mask shape & gradient. Here's the compositing code, a function called drawList:

var r = ctx.createLinearGradient(1100, 0, 1200 + stretch,0);
r.addColorStop(0,"rgba(255, 255, 255, 1.0");
r.addColorStop(0.8,"rgba(255, 255, 255, 0.0");
ctx.fillStyle = r;
ctx.globalCompositeOperation = "source-in";
ctx.fillRect(300, 0, 1200 + stretch, 1000);

stretch starts at 0 and increases in an animating function using requestAnimationFrame, which I have verified works when animating a simple rectangle or even ctx.clearRect(300, 0, 1200 + stretch, 1000). The animation function calls drawList on each iteration and the drawList.

Upon loading the list text from drawList fades according to the gradient fill of the rectangle above. The problem I'm running into is that as soon as the animation function is called, the canvas is erased and doesn't redraw the list. I would use ctx.clip(), but I really need the gradient fill to create the fade effect.

Any ideas? Is this a limitation of canvas?


Solution

  • When setting canvas context states such as globalAlpha, globalCompositeOperation, clip, etc... these states remain active and can affect the rest of your rendering. One way to solve this is to set all the states again after you have used them. This can end up in a lot of extra code so the 2Dcontext API provides two handy functions to control the state. ctx.save() and ctx.restore()

    ctx.save() pushes the current state to a stack. ctx.restore() pops the last saved state and sets the canvas context to that state. You can nest saves, but remember to match each save with a restore.

    For more info see ctx.save() at MDN

    A word of warning. Saving and restoring states should be avoided if you are after high performance rendering for realtime games and interfaces. State changes can be a stall point when rendering, or just replicate unneeded state changes. Restoring a state may force the GPU to reload from CPU memory a bitmap used by ctx.createPattern() in a previously saved state, even if you are not intending to use it. This can be very slow and have a major performance hit on your frame rate, especially if you keep restoring to that unused pattern.