How to pixelate an image with canvas and javascript
Asked Answered
W

2

32

I've been experimenting a bit with the canvas element and was curious how to pull off an effect.

I've somewhat got what I'm looking for from a collection of tutorials and demos, but I need some assistance getting the rest of the way there. What I'm looking for is to pixelate an image on mouseover, then refocus/un-pixelate it on mouseout. You can see a good example of the effect at http://www.cropp.com/ when mousing over the blocks that are below the main carousel.

Here is a link to a fiddle I started. The fiddle won't work because you can't use cross domain images (womp womp), but you can still see my code thus far. When mousing over my canvas object I'm able to pixelate the image, but it's kind of backwards to what I'm attempting to get. Any help or advice would be greatly appreciated.

var pixelation = 40,
    fps = 120,
    timeInterval = 1000 / fps, 
    canvas = document.getElementById('photo'),
    context = canvas.getContext('2d'),
    imgObj = new Image();

imgObj.src = 'images/me.jpg';
imgObj.onload = function () {    
    context.drawImage(imgObj, 0, 0);
};

canvas.addEventListener('mouseover', function() {
    var interval = setInterval(function () {
        context.drawImage(imgObj, 0, 0);

        if (pixelation < 1) {
            clearInterval(interval);
            pixelation = 40;
        } else {
            pixelate(context, canvas.width, canvas.height, 0, 0);
        }
    }, timeInterval);
});

function pixelate(context, srcWidth, srcHeight, xPos, yPos) {

    var sourceX = xPos,
        sourceY = yPos,
        imageData = context.getImageData(sourceX, sourceY, srcWidth, srcHeight),
        data = imageData.data;

    for (var y = 0; y < srcHeight; y += pixelation) {
        for (var x = 0; x < srcWidth; x += pixelation) {

            var red = data[((srcWidth * y) + x) * 4],
                green = data[((srcWidth * y) + x) * 4 + 1],
                blue = data[((srcWidth * y) + x) * 4 + 2];

            for (var n = 0; n < pixelation; n++) {
                for (var m = 0; m < pixelation; m++) {
                    if (x + m < srcWidth) {
                        data[((srcWidth * (y + n)) + (x + m)) * 4] = red;
                        data[((srcWidth * (y + n)) + (x + m)) * 4 + 1] = green;
                        data[((srcWidth * (y + n)) + (x + m)) * 4 + 2] = blue;
                    }
                }
            }
        }
    }

    // overwrite original image
    context.putImageData(imageData, xPos, yPos);
    pixelation -= 1;
}
Whipstall answered 2/10, 2013 at 3:43 Comment(1)
Updated your fiddle to have an actual image, via a data url: jsfiddle.net/xDt7U/1Skvorak
S
49

You don't need to iterate pixel buffer to create a pixelating effect.

Simply turn off image smoothing and enlarge a small version of the image to the canvas. This will also mean you can use any image as source (CORS-wise).

Example:

Fiddle demo

// get a block size (see demo for this approach)
var size = blocks.value / 100,
    w = canvas.width * size,
    h = canvas.height * size;

// draw the original image at a fraction of the final size
ctx.drawImage(img, 0, 0, w, h);

// turn off image aliasing
ctx.msImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;

// enlarge the minimized image to full size    
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, canvas.width, canvas.height);

In the demo you can animate this effect to see that the performance is very good compared to an pixel iterating method as the browser takes care of the "pixelation" internally in compiled code.

Sinfonia answered 2/10, 2013 at 4:5 Comment(8)
Wow that looks great, and far less code than what I had going. Would you mind explaining the code a bit perhaps? I understand what the pixelate method is doing, but a bit curious about how toggleAnim works and the requestAnimationFrame. How could I get that function to animate to a certain point (pixelated) and then stop. Or vice versa?Whipstall
@Whipstall toggleAnim is just part of the demo to animate the pixelation to get an impression of the performance. I updated the demo with inline comments. requestAnimationFrame is a low-level animation method that allows you to synchronize animations to monitor updates to make it smoother and more performant. It works better than setTimout/setInterval.Sinfonia
@ken-abdias-software I appreciate the comments. If you have another min and are willing to help could you take a look at this fiddle. I've got it working on mouseover/out but it feels dirty. For instance if I rapidly mouse over and then out the transition doesn't look smooth. The mouse over function continues to run at that point. Any suggestions on how I could get it to smoothly transition? For example if I rapidly hover and then mouse out the transition out is from whatever point the hover effect got to. Hopefully that makes some sense.Whipstall
@ken-abdias-software Ah, I see what you did there with moving a few things to be globally accessible as well as the isPlaying flag. Makes perfect sense now. Definitely a lot cleaner than what I had put together. Thanks a lot for all your help!Whipstall
@ken-abdias-software Quick question regarding aspect ratios that are driving me a bit nuts. My canvas has fixed dimensions of 250x250 (1:1 ratio). The image however could possibly be any size/ratio. Obviously if I try to force the image to be 250x250 it looks distorted. I've attempted to make it respect the aspect ratio but I get a strange overlap in the canvas object now. Something is off in my math or how I'm applying it to the canvas. If you have a moment could you check out my updated fiddle and see if you notice anything jumping out at you?Whipstall
@Whipstall sure, but I would suggest you open it as a new question as it is out of scope for this one.Sinfonia
@ken-abdias-software Agreed. And I'm sure it would make it easier to help someone else with the same problem in the future. Here is the new questionWhipstall
It seems this answer was converted to a full-blown library: github.com/43081j/pixelate.jsMichikomichon
U
8

Just keep in mind, there is a few javascript libraries doing same effect, for example pixelate or close-pixelate.

Upsurge answered 18/9, 2014 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.