canvas clearrect, with alpha - javascript

So I know that context.clearRect makes pixels transparent, but I'm wondering, is there a function to make pixels translucent?
For example, say I have a canvas with these colors (fourth one in each color is alpha):
#ffff #feef #abff
#5f6f #000f #ffff
Running clearRect would resolve into this (or something, just make them all transparent):
#fff0 #fee0 #abf0
#5f60 #0000 #fff0
I want to remove opacity, but not make it transparent (kind of like globalAlpha for clearRect), so that it can end up like this (lets say I set the globalAlpha equivalent to 0.5):
#fff8 #fee8 #abf8
#5f68 #0008 #fff8
Is this possible? Or would it be simpler just to draw everything on an off-screen canvas, then draw that canvas (with globalAlpha set) on an on-screen one?
Let me know if this isn't clear in any way.

The answer above gets the job done, however getImageData is super slow and if you have a lot of other stuff going on it will slow down your animation immensely. If you create a second off screen canvas element you can set its global alpha to .9 and shuffle them back and forth and get the same effect with much greater speed.
context2.clearRect(0,0,width,height);
context2.globalAlpha = .9;
context2.drawImage(canvas1,0,0);
context1.clearRect(0,0,width,height);
context1.drawImage(canvas2,0,0);
context1.the rest of the drawing that you are doing goes here.

I just tried to figure this out too, and I've decided to count through the pixels, setting the alpha channel of each one manually. This is a bit more work, because I can't just cover the entire canvas with a rect, but it's working so far.
I'm doing this so that I can set a background image for the webpage and put my canvas animation over it, without having to draw the background in the canvas element.
var oldArray = context.getImageData(0,0,canvas.width,canvas.height);
//count through only the alpha pixels
for(var d=3;d<oldArray.data.length;d+=4){
//dim it with some feedback, I'm using .9
oldArray.data[d] = Math.floor(oldArray.data[d]*.9);
}
sw.context.putImageData(oldArray,0,0);

Related

Trailing fade to complete black with PIXI.js and WebGLRenderer

I have application made using PIXI.js and it uses a WebGLRenderer.
I'm preserving the drawing buffer and not clearing before render:
{ preserveDrawingBuffer: true, clearBeforeRender: false }
This allows me to create trails as objects move around.
I want the trails to fade out over time, so I apply a transparent black rectangle on top over every rendering. This works, but the fade out eventually rounds off to gray. I want a complete fade to black.
I've tried using a ColorMatrixFilter filter with a lowered brightness on my root container, hoping it would cause a fade effect. It didn't cause any fade effect, instead just causing everything to be slightly darker. If that had worked, then a custom filter could help to do the job.
How can I get a slow gradual fade to complete black for the trails of my rendered objects?
EDIT: Here are a few examples of what I've tried:
// `this` being my app object.
this.fadeGraphics = new PIXI.Graphics()
this.root.addChild(this.fadeGraphics)
// Blend Mode
this.fadeGraphics.blendMode = PIXI.BLEND_MODES.MULTIPLY
this.fadeGraphics.beginFill(0xf0f0f0)
this.fadeGraphics.drawRect(0, 0, this.screenWidth, this.screenHeight)
this.fadeGraphics.endFill()
// Transparent black rectangle.
this.fadeGraphics.beginFill(0x000000, .05)
this.fadeGraphics.drawRect(0, 0, this.screenWidth, this.screenHeight)
this.fadeGraphics.endFill()
Both these methods leave me with a gray trail, the trail goes away if my values are strong enough. Though, I want a very long-term trail so I have to use small values, and possibly also apply them every nth frame.
I think a SUBTRACT blend mode might be able to do what I need.
Unfortunately it doesn't seem available in Pixi.js.
I eventually figured out that fading to white works wonderfully.
Thus, one solution is to fade to white, then invert color, and rotate hue 180 degrees.
You can do this with a CSS filter on your canvas, though it doesn't work in all browsers, has a performance hit, and all your color intensities get inverted.

Javascript & Canvas: Draw and delete lines to create a "breathing" circle

I would like to create an element, that shows a red circle. Once the user clicks on it, she can record her voice. In order to show the LIVE mode, I'd like to make the circle "breath" according to the incoming frequencies.
I'm experimenting with a <canvas> element. That means it creates a circle that gets bigger and smaller, depending on the variable arcrad. However, the lines are being drawn correctly, but they do not disappear afterwards. I tried to apply .clip() but can't get it to work...
if (arcrad <= 10) arcrad = 10;
analyserContext.beginPath();
analyserContext.arc(100,120,arcrad,0,2*Math.PI);
analyserContext.closePath();
analyserContext.lineWidth = 2;
analyserContext.strokeStyle = 'red';
analyserContext.stroke();
Any ideas - or completely different strategies for this use case?
Canvas will overdraw by default. For your animation you’ll need to clean the canvas at the start of each frame. Use something the following at the start of your drawing function:
analyserContext.clearRect(0,0,200,200);
assuming your canvas is 200 pixels wide and high. It’s worth pointing out that sometimes you don’t want to completely clear the animation field every frame. For example, if you were to draw a semi transparent rectangle over the frame at the beginning (instead of clearing it) then you’d end up with a basic ‘bullet time’ style effect.
It's a normal behavior. Once something it's drawn on the canvas, it's there forever. You have to think like if you were painting something: what has been done cannot be undone.
Luckily, you still have solutions:
1) redraw another circle on top of the first one with the background color. It's really not the recommend way, but it still can be useful
2) use clearRect method (see How to clear the canvas for redrawing)
There are numerous ways to clear a canvas pre drawing to create animation:
How to clear the canvas for redrawing
simplest in my mind:
canvas.width=canvas.width;
though can equally use clearRect (which is actually quicker and won't reset the entire canvas if that is an issue regarding transforms etc!) over the region or whole canvas.
Get the likes of:
http://jsfiddle.net/dw17jxee/

Directly set every point in path's alpha channel on canvas

Let's say I have a canvas with stuff on it, and I want to carve an ellipse in it's center, whose all of the pixels have an alpha of 0, to let everything behind the canvas to pass through, and given that previously the area now filled by the ellipse was something else, and said that, for performance reasons, I don't want to use putImageData, how would I do so?
And if there isn't a way, how would I carve an ellipse using putImageData?
Just to make it clear: I don't want to draw the html elements on the canvas, I want them to simply be in html behind it
you can use context.globalCompositeOperation = 'destination-out' after you draw the frame to fill the clipped shape(s).
example: http://jsfiddle.net/rlemon/6nEpc/

Why do images lose quality after the context has been rotated?

I'm making a top-down shooter game that relies on the avatar always being rotated pointing to the mouse cursor. I achieve rotation like this:
//Rendering.
context.save(); //Save the context state, we're about to change it a lot.
context.translate(position[0] + picture.width/2, position[1] + picture.height/2); //Translate the context to the center of the image.
context.rotate(phi); //Rotate the context by the object's phi.
context.drawImage(picture.image, -picture.width/2, -picture.height/2); //Draw the image at the appropriate position (center of the image = [0, 0]).
context.restore(); //Get the state back.
When the phi is zero, the image is rendered in its normal quality, with sharp edges and detectable pixels. But, when I set the phi to a nonzero value (actually, when it's not 0, Pi/2, Pi, Pi+Pi/2 or 2Pi), the image looses it's sharpness and the individual pixels can't be seen anymore, because they are blurred out.
Here's a screenshot (sorry about the general bad quality of the screenshot, but I think that the difference is more than noticeable):
This is, well, a bit unacceptable. I can't have the images always blurred out! Why is this happening and can I solve it?
You could try
context.imageSmoothingEnabled = false;
See docs:
context.imageSmoothingEnabled [ = value ]
Returns whether pattern fills and the drawImage() method will attempt to smooth images if they have to rescale them (as opposed to just rendering the images with "big pixels").
Can be set, to change whether images are smoothed (true) or not (false).
If you want a true pixel-art retro style effect, you'd need to manually create rotated sprite images for several angles, look up the appropriate sprite for the current value of phi, and draw it without rotation. This obviously requires a fair amount of art work!
IF you are rotating images around their center point, make sure the image itself has an even number of pixels. Once you end up on odd coordinates the image data needs to be interpolated for the target canvas. Apple has some nice documentation on translating and rotating the canvas.
So for any image, as suggested above use rounding to snap to full pixels.
context.translate(Math.floor(img.width/2), Math.floor(img.height/2));
This way every source pixel of your image will always be drawn exactly into a pixel inside the canvas and blurring does not occur. This however is only true for multiples of 90 degrees.
It seems that all browsers do, to some extend, antialiasing in image drawing so you will probably have to provide rotated images as sprites.
According to this Chromium bug report you might be lucky there if they haven't fixed it yet. Read through and you'll learn that Ian Hickson likely opposed making antialiased image drawing optional.
(picture.width/2, picture.height/2) point won't always work.
(Math.floor(picture.width/2) + 0.5, Math.floor(picture.height/2) + 0.5) should help.
Well, actually it is something you cannot get around
If you rotate an image by a multiple of 90 degrees, your library should smart enough so that no interpolation is applied.
But as soon as you rotate an image by an angle different from a multiple of 90 degrees, you need to interpolate. As a consequence, you get that smoothing. If you are interested in the theory, you may look for a book on computer graphics or image processing.
For the concrete case of image rotation you may have a look at this paper,
http://bigwww.epfl.ch/publications/unser9502.html

html canvas motion blur with transparent background

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
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.
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.
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.
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!

Categories

Resources