copy non transparent pixels only to HTML5 canvas - javascript

I am writing a colouring game for small children, where I have a black and white image shown on a canvas initially, and as the user moves the drawing tool (mouse) over the canvas, the black and white surface gets over-painted with the colour information from the corresponding coloured image.
In particular, on every mouse move I need to copy a circular area from the coloured image to my canvas. The edge of the circle should be a little blurry to better immitate the qualities of a real drawing tool.
The question is how to accomplish this?
One way I see is to use a clipping region, but this approach does not let me have blurry edges. Or does it?
So I was thinking about using an alpha mask to do that and copy only pixels that correspond to the pixels in the mask that have non zero alpha. Is it feasible?

My suggestion is to have your drawable canvas in front of the coloured image you wish to reveal. (You could use your coloured image as a CSS background image for the canvas.)
Initially have the canvas containing the black and white image with 100% opacity. Then, when you draw, actually erase the contents of the canvas to show the image behind.
Like this:
var pos_x, pos_y, circle_radius; // initialise these appropriately
context.globalCompositeOperation = 'destination-out';
context.fillStyle = "rgba(0,0,0, 1.0)";
// And "draw" a circle (actually erase it to reveal the background image)
context.beginPath();
context.arc(pos_x, pos_y, circle_radius, 0, Math.PI*2);
context.fill();

I would probably use multiple clipping regions with varying alpha (one dab for each) to mimic the effect you are after. Render the low opacity one first (paste using drawImage) and render the rest after that till you reach alpha=1.0.

Have you considered using radial gradients that go from an opaque color to a fully transparent one?
Here is a demo from Mozilla. The circles are drawn the way you need. - https://developer.mozilla.org/samples/canvas-tutorial/4_10_canvas_radialgradient.html

Related

Unwanted tiling effect in HTML5 canvas when zooming

I'm using html5 canvas to manipulate individual pixels. The canvas is zoomable and draggable, and each pixel is a rectangle, basically I do the following:
pixels.forEach(p => {
context.fillStyle = p.color;
context.fillRect(toScreenX(p.x), toScreenY(p.y), 1*scale, 1*scale);
});
where toScreenX and toScreenY determine the position of the rectangle based on the position of the pixel and the actual scale and offset.
When the zoom scale is 1, everything is okay as shown on picture below:
However, when I start to zoom in, thin white lines start to appear between pixels, and the picture gets distorted as shown in the two figure below:
Here's a short video about the effect: https://imgur.com/a/dvEaZIy
I also tried to use context.putImageData instead of fillRect but for individual pixels, it gets terribly slow.

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/

Change GIF color with JavaScript

I'm building a website and I need a user to be able to select an image (a transparent gif with black sign on it) and select a color. I have all the gifs in black with transparent background.
How can I change the color of the gif (the black only) to the color the user chose? I was thinking about using a canvas for this but I'm not sure...
You can use a canvas for this. There is no need to iterate the pixel buffer as many suggests and I would recommend avoiding if possible for two reasons:
CORS restriction may apply if image is loaded from a different origin
Performance
There is a much easier way involving composite modes:
Live demo
/// load image here, then:
function render() {
/// this composite mode clears the canvas as well
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(img, 0, 0);
/// this mode fills in whatever, in the image
ctx.globalCompositeOperation = 'source-in';
/// color to change GIF to:
ctx.fillStyle = '#00c';
/// fill color into non-alpha pixels
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
The copy mode works here as the image is the only thing we want to draw to the canvas. If you need to draw in other details as well then use source-over instead and manually clear the canvas using clearRect().
Tip: You can also draw another image on top instead of a fill color.
Original GIF
Changed to blue color
ctx.fillStyle = '#00c';
Changed to red color
ctx.fillStyle = '#c00';
etc.
Assuming that you want to change the color of the black parts of the image (not the transparent parts) you'll have to use canvas to get the black pixels of the image and draw a new image replacing these pixels with the color your user has chosen.
If you want to replace the transparent parts, simply setting a background color using CSS will do the trick.

Canvas element 'rubbing out' effect with JavaScript

I have a canvas <canvas></canvas> that displays a graphic of an unclean window. I need to be able to 'clean' the window with my cursor, displaying another image behind it. Just as this website uses their brushes: http://mudcu.be/sketchpad/ but rather than adding to the page, I need to remove content to display the image behind it.
Here's an example of the before and after 'rubbing out':
http://www.titaniumwebdesigns.com/images/forums/before.png http://www.titaniumwebdesigns.com/images/forums/after.png
See this complete DEMO
globalCompositeOperation is one of the most nice features in canvas api.
To achieve the desired effect, I use multiple canvas layers and globalCompositeOperation.
In this solution we have 3 layers:
Layer 1 - bottom Layer 2 - middle
Layer 3 - top
Middle and Top layers are static and only the middle layer is dynamically cleared using globalCompositeOperation.
middleCtx.globalCompositeOperation = "xor";
With globalCompositeOperation = "xor", the brush is drawn over the layer and clears the portion of canvas where it was drawn.
The final result is:
UPDATE:
To verify if the window is fully cleaned I create a hidden canvas with the same size of the others layers and drawn a black rectangle on it. When dragging the mouse over the canvas the layer 2 (middle) is cleared with a circle with transparent gradient color and now we also draw over the hidden canvas a circle with white color (might be any color different of black).
So on, we just verify the pixels of the hidden canvas and if there is no black pixels, the window is cleaned.
To get the image data we need to use something like:
imageData = context.getImageData(x, y, width, height)
and then get the pixels from the image data:
pixels = imageData.data;
The requestAnimationFrame routine is used for performance reason, because getImageData might be slow. The major change in the code is put the brush action inside an animation frame when dragging the mouse instead of do that action in each mouse move event. This allows the processor to have more time to do the pixel data verification.
Here is the modified fiddle with pixel data verification and an alert when the window is cleaned:
jsFiddle link
If you have a canvas where you have drawn a blurred image into it once, you should be able to create that effect by creating a "brush" image (an image containing a semi-transparent circle, with soft edges), and then draw that image in the canvas at the mouse coordinate using:
canvasContext.globalCompositeOperation = "destination-out";
https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html
As soon as you have drawn the blurred image to the canvas, you just need to call the line above once and all drawn images after will use the specified composite operation.

canvas clearrect, with alpha

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);

Categories

Resources