4

I just created a fancy canvas effect using cheap motion blur

ctx.fillStyle = "rgba(255,255,255,0.2)";
ctx.fillRect(0,0,canvas.width,canvas.height);

Now i want to do the same, but with transparent background. Is there any way to do something like that? I'm playing with globalAlpha, but this is probably a wrong way.

PS: Google really don't like me today

Frizi
  • 2,900
  • 1
  • 19
  • 25
  • 1
    You are basically filling the canvas with a semi-transparent color, no motion whatsoever, aren't you? – pimvdb Mar 14 '11 at 20:54
  • As i said, its really "cheap" effect, but applied on objects in motion, looks really nice. But now i must do something like global alpha changing, fade out. Only thing i can imagine is iterating on every pixel, but its really to slow (i have simple physics applied for now). – Frizi Mar 14 '11 at 20:59
  • I might be overlooking something, but please try using `setInterval` and fill the first time with `0.1`, then `0.2` etc. – pimvdb Mar 14 '11 at 21:01
  • i posted only 2 lines of code, but a whole script is a big real-time loop with physics update and draws. For now i do this (what i posted) instead of clearing whole screen. It works, but only when i have white background. Im trying to find a way to do something, like fadeRect(0.2, ...); – Frizi Mar 14 '11 at 21:26
  • 2
    Your motion blur effect was very popular when there were no advanced shaders and similar. The other commenter are simply not aware of that ;-) – Omiod Mar 14 '11 at 22:59
  • @pimvdb it doesn't look like this can be done with composite methods to me. What do you think? – ellisbben Mar 29 '12 at 17:27

4 Answers4

4

Here's a more performance friendly way of doing it, it requires an invisible buffer and a visible canvas.

buffer.save();
buffer.globalCompositeOperation = 'copy';
buffer.globalAlpha = 0.2;
buffer.drawImage(screen.canvas, 0, 0, screen.canvas.width, screen.canvas.height);
buffer.restore();

Basically you draw your objs to the buffer, which being invisible is very fast, then draw it to the screen. Then you replace clearing the buffer with copying the last frame onto the buffer using the global alpha, and globalCompositeOperation 'copy' to make the buffer into a semi-transparent version of the previous frame.

hobberwickey
  • 6,118
  • 4
  • 28
  • 29
2

You can create an effect like this by using globalAlpha and two different canvas objects: one for the foreground, and one for the background. For example, with the following canvas elements:

<canvas id="bg" width="256" height="256"></canvas>
<canvas id="fg" width="256" height="256"></canvas>

You could copy draw both a background texture and a motion blurred copied of foreground like so:

bg.globalAlpha = 0.1;
bg.fillStyle = bgPattern;
bg.fillRect(0, 0, bgCanvas.width, bgCanvas.height);

bg.globalAlpha = 0.3;
bg.drawImage(fgCanvas, 0, 0);

Here is a jsFiddle example of this.

OP asked how to do this with an HTML background. Since you can't keep a copy of the background, you have to hold onto copies of previous frames, and draw all of them at various alphas each frame. Nostalgia: the old 3dfx Voodoo 5 video card had a hardware feature called a "t-buffer", which basically let you do this technique with hardware acceleration.

Here is a jsFiddle example of that style. This is nowhere near as performant as the previous method, though.

Nathan Ostgard
  • 8,258
  • 2
  • 27
  • 19
  • okay, it is really good, but what when i have html content under my canvas? – Frizi Mar 15 '11 at 17:20
  • @Frizi To do that, you would have to save a copy of the previous canvas frames, and draw all of them at various alphas each frame. I've updated the question with an example. – Nathan Ostgard Mar 15 '11 at 18:02
  • I think the only thing i can do to achieve better performance is just drawing objects with "back history", implement motion blur into animation, but it is hardly possible. Your example are very interesting. Thanks a lot. – Frizi Mar 15 '11 at 18:52
0

What you are doing in the example is partially clear the screen with a semi transparent color, but as it is, you will always gonna to "add" to the alpha channel up to 1 (no transparency).

To have this working with transparent canvas (so you can see what lies below) you should subtract the alpha value instead of adding, but I don't know a way to do this with the available tools, except running all the pixels one by one and decrease the alpha value, but this will be really, really slow.

Omiod
  • 11,285
  • 11
  • 53
  • 59
0

If you are keeping track of the entities on screen you can do this by spawning new entities as the mouse moves and then setting their alpha level in a tween down to zero. Once they reach zero alpha, remove the entity from memory.

This requires multiple drawing and will slow down rendering if you crank it up too much. Obviously the two-canvas approach is the simplest and cheapest from a render performance perspective but it doesn't allow you to control other features like making the "particles" move erratically or apply physics to them!

Rob Evans
  • 6,750
  • 4
  • 39
  • 56