Applying Gaussian Blur to HTML elements with CSS
Hey there. So, I recently purchased my first Android-powered phone computer (well, technically it was free with the 2 year contract) and while powering it off I noticed the background of the power-off confirmation screen has a gentle gaussian blur. This blurred-background UI is common in desktop (and smart-phone) applications, but not on the web.
Unfortunatly, we don't have a "blur" CSS property (yet) that we can apply to arbitrary elements, and canvas just doesn't cut it. So, if we want to implement a blurred background effect we have to provide an image of a blurred background. "But wait!", you say, "what about SVG?". I love html5boilerplate, I love short doctypes, I love text/html - SVG filters just aren't implemented completely in the HTML5 environment.
But what we do have, at least in Firefox 4, is background-image:-moz-element(#elementID).
New in Firefox 4, the -moz-element extension to the background-image property of elements allows us to display an element, and its descendants, including cross-domain iframes, as a background image. I'm not going to go over the restrictions and details of its implementation; you can read about it on the mozilla hack blog.
What I'll be showing you isn't really gaussian blur, get over it (it is probably some form of box blur).
Check out the Result tab in the jsfiddle for the demo, view the CSS for the good-stuff.
Note: THIS WILL ONLY WORK IN FF4!
See the Pen mGnBE by David Murdoch (@davidmurdoch) on CodePen.
So, what is happening here?
First, we reference our #content element via background-image's -moz-element extension, like this:
We then take advanatage of CSS3's multiple background-image support and add the additional -moz-elements required to render our blur.
To calculate how many -moz-elements you need:
numberOfElements = ( (radius * 2) + 1 )2 ) - 4
where radius > 0.
So, a radius of 1px gives needs 5 elements, 2 = 21, and 3 = 45.
We then need to position our background-images. The coordinates for a 1px radius are:
(0,0) (0,1) (0,-1) (1,0) (-1,0)
and 2px adds on an additional 16 coordinates:
(1,1) (1,-1) (-1,1) (-1,-1) (0,2) (0,-2) (2,0) (2,1) (2,-1) (-2,0) (-2,1) (-2,-1) (1,2) (1,-1) (-1,2) (-1,-2)
...and so on.
Once we've got our background-images in place we need to adjust the opacity of the original element (#content):
opacity = 1 / numberOfElements;
So, for a 2px radius the opacity should be .048 (1px = , 3px = .22)
It should be pretty easy to write a plugin or script to generate the neccessary CSS for you; but I leave that up to the reader (aka I'm too lazy to do it right now).
Disclaimer: This can really hurt performance with a large blur radius. You may be able to omit some -moz-elements if performance becomes an issue.