Make a Depth of Field Effect with CSS Animation

2016-08-19 00:00:00 by Mike

It turns out that simulating depth-of-field with HTML and CSS is surprisingly easy. That's right, HTML and CSS, and no JavaScript. The full demo can be seen here.

There are two approaches described in this article. The first approach, shown in Figure 1 below, uses two transparent PNGs. One PNG serves as the background (i.e.: things far away from the camera), the other PNG, for the foreground, is absolutely position on top of the background image. Then a CSS blur animation is applied to both images with two selectors. Two selectors are required to create the alternating blur and focus between the two images. When the foreground is in focus, the background is blurred and vice versa.

The second approach to creating depth-of-field uses SVG clip path to define a polygonal shape to blur. In Figure 2 below, the SVG clip path, outlined in orange defines a foreground area to which we can apply the CSS blur selector for an animation.

Each approach is functional in modern browsers. It is worth noting that the second approach, with SVG, is more performant. This is because one image is used and that image is a JPG, much smaller in file size. The other approach uses two PNGs, significantly larger in size. Thanks reddit user tjuk for pointing this out!

Figure 1: Two absolutely positioned PNG images. The foreground image outlined in blue and the underlying background image.
Figure 2: One image with an SVG clip path defined (outlined in orange).

<section class="container"> <img src="./images/lightfield-midground.png" class="midground midground-blur"> <img src="./images/lightfield-foreground.png" class="foreground blur"> </section>
HTML for approach 1: Two absolutely positioned images with alternating blur animations.

<div style="position:relative"> <img src="./images/bat-image-map.jpg" style="position:absolute" class="midground midground-blur"> <svg viewBox="0 0 1080 1080" width="1080" height="1080" xmlns="" xmlns:xlink="" style="position:absolute" class="foreground blur"> <defs> <clipPath id="c"> <polygon points="5,497,81,481,90,366,794,191,896,306,1078,276,1076,1077,4,1078" /> </clipPath> </defs> <image xlink:href="./images/bat-image-map.jpg" width="1080" height="1080" clip-path="url(#c)"/> </svg> </div>
HTML for approach 2: One JPG image referenced twice in the HTML. First for the background, and secondly as a clipped SVG polygon.

.container { position:relative; height:450px; overflow:hidden; width:900px; } .container > img { position:absolute; width:1029px; } .container > img.foreground { top:220px; left:-20px; } .blur { -webkit-animation: blur 5s infinite; -moz-animation:blur 5s infinite; } .midground-blur { -webkit-animation: midground-blur 5s infinite; -moz-animation: midground-blur 5s infinite; } @-webkit-keyframes blur { 0%, 100% { -webkit-filter: blur(0px); } 50% { -webkit-filter: blur(5px); } } @-moz-keyframes blur { 0%, 100% { filter: blur(0px); } 50% { filter: blur(5px); } } @-webkit-keyframes midground-blur { 0%, 100% { -webkit-filter: blur(5px); } 50% { -webkit-filter: blur(0px); } } @-moz-keyframes midground-blur { 0%, 100% { filter: blur(5px); } 50% { filter: blur(0px); } }
CSS for the blur animation: Fairly standard CSS for the animation and absolute positioning.