eleqtriq

Sorry, looks as if your Browser can't display SVG

We all know that SVG filters are the best thing since season 4 of "Breaking Bad", with their full potential still to to be discovered. In this article I will show how to create effects like true IOS translucency or tilt-shift/focus blur effects, apply them with one line of CSS and how to animate them.

What you will learn:

We will create an SVG filter that can be applied to any element with one line of CSS.This filter will be constructed in a way that it affects some areas stronger than others. We won't use complicated hacks like cloned elements or multiple versions of one image or any form of javascript trickery.

A masked CSS filter, the "good" way:

This is the recommended method which, unfortunately, doesn't work in Firefox. I will show how to cope with this bug later in the article, but for the moment better do not open these demos in Firefox. We start with a cute little SVG, containing some geometry:

unfiltered SVG

The unfiltered SVG graphic we are going to apply the partial blur to. Click to open.

...then add a filter and apply it to the graphic:

(SVG stuff happening here) ... <filter id="filter"> <feGaussianBlur stdDeviation="10" result="BLUR"/> <filter/> ... (More SVG stuff)
Filtered SVG

Same graphic, now a filter is applied. Click to open.

Right now the graphic is blurred completely. Our goal is to have only parts of the graphic inside a rectangular area blurred and leaving the parts outside unfiltered. We insert a rect into our SVG to use it as a mask for the blur:

... <defs> <rect id="rect" width="100%" height="100%" /> <filter id="filter"> ...

To be able to use this rect as a mask we must find a way to get it into our filter. The wise inventors of SVG gave us feImage for that: ... <filter id="filter"> <feGaussianBlur stdDeviation="10" result="BLUR"/> <feImage id="feimage" xlink:href='#mask' result="MASK" /> ...

With the rect element now being referenced from inside the filter we can start playing. We'll use feComposite to "cut off" the blurred parts outside of the rect: ... <feComposite in="blur" in2="mask" operator="in" result="COMP" /> ...

Filtered SVG

Filter is masked, but unfiltered elements are hidden. Click to open.

The filtered area now appears cropped, but how can we make our filter show the unfiltered parts of the graphic as well?

feMerge will help us to combine the unfiltered (the "sourceGraphic") and the filtered parts of the source element:

... <feMerge result="MERGE"> <feMergeNode in="SourceGraphic" /> <feMergeNode in="COMP" /> </feMerge> ...

Filtered and masked SVG

Unfiltered and filtered SVG merged together. No image duplication or javascript hackery involved. (Click to open).

This is quite a nice little trick to create iOS style translucency and of course the mask can be animated with CSS transitons and animations:

Animated SVG filter mask on hover.

Demo of an animated, masked filter. (Click to open).

<defs> <style> /* <![CDATA[ */ #mask{ animation: scaleme 2s ease-in-out infinite; } @keyframes scaleme { ... } /* ]]> */ </style> <rect id="mask" width="100%" height="100%" /> ... </defs>

What about Firefox?

The final result looks like what we had in mind firsthand. It works in every modern browser, except Firefox. Because of an old bug, firefox can't access internal SVG fragments within SVG filters. Let's find a workaround for this problem:

The most obvious solution for Firefox of course is to put the mask into an external SVG and reference it from the feImage primitive: ... <feImage id="feimage" xlink:href='/path/to/my.svg' result="MASK" /> ...

Alternatively we must find a way to create the mask within the filter. Michael Mullany found a brilliant way to create an alpha mask by "misusing" the feDiffuseLighting filter primitive and build a radial gradient mask, but most geometric forms like polygons or curves can't be made this way. An alternative way to circumvent Fox' current limitation is to use an xLinked dataURI as a mask:

... <feImage id="feimage" xlink:href='data:image/svg+xml;charset=utf-8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100"><rect width="100%" height="100%" /></svg>' x="192px" y="192px" width="16px" height="16px" result="mask" preserveAspectRatio="none"/>

(The data URI should be URL encoded as some browsers will not be able to read it otherwise.)

Unfortunately it is not possible to apply a CSS animation to the feImage primitive. This makes animating the blurred area slightly more difficult, as we must manipulate the filter feImage attribute directly. This is not possible with CSS, so with the help of GSAP Tweenlite we will apply a Javascript animation: ... <script> TweenLite.to(feimage, .8, {ease: Bounce.easeOut, attr:{x:"20", y:"20", width:"360", height:"360"}}); </script> ...

Universal demo: iOS like translucency working in every modern browser. (Click to open).

Applying the filter to HTML content

Some browsers allow us to reference SVG filters from an SVG and apply it to HTML content. Open the demo below and try to select and copy the text (Blink only):

Partial blur applied to HTML content (Click to open).

... .myHTML-element{ filter: url(#myfilter); -webkit-filter: url(#myfilter); } ...

IE and Edge will show the unfiltered content though. Safari will not display the filtered element at all, a very serious accessibility bug. Make sure you do have some Javascript workaround for Safari.

Creating sophistcated image filters

The techniques shown above can be used to create complex effects on images. Let's take a closer look how to create a tilt shift/focus filter by masking a blur filter.

Below you see the SVG that we will use as a mask. It contains two rectangles that are filled with a radial and a linear gradient from white to transparent:

Preview of the SVG mask (Click to open).

We will apply the masked filter to this image:

Here's the same image with a partial blurred svg filter applied:

The final version: An SVG containing a bitmap and a filter (Click to open).

Downloads & Links

Trackback

2 Responses to “SVG Partial Blur and iOS Style Translucency”

  1. Pingback by Web Development Reading List #117: Storytelling, Security in Devtools and 350ms Tap Delay | DigitalMofo 12/18/2015

    […] SVG, you can achieve partial blur and iOS style translucency17. All done with CSS filters and some little […]

  2. Comment by Pius 04/07/2016

    Thanks for writing this article! I also enjoyed your article in Smashing Magazine.

    I wrote a tool recently for experimenting with applying various filter effects to an image in the browser. Each filter is implemented twice – once with CSS/SVG and once by manipulating pixels in an HTML canvas (so you can save the image if it’s not cross-origin).

    A couple techniques you might like: (1) A polar / reverse polar transform can be done in SVG with feImage and feDisplacementMap. I’m dynamically generating two displacement map images at different scales for slightly better results. So you can do a spin (angular) or zoom (radial) blur by applying a polar transform, followed by either a horizontal or vertical blur, followed by a reverse polar transform.

    And (2) each filter can be applied to only one or two color channels. I implemented this by zeroing the unfiltered channels of the filter result (using feComponentTransfer and feFunc{R,G,B} with type=”linear” and slope=”0″ – feColorMatrix would work too), then zeroing the filtered channels of SourceGraphic, and combining the two using feComposite with operator=”arithmetic” and k2=”1″ and k3=”1″.

    For example, here’s an angular blur effect on the green and blue channels of the image you used at the end of this article (aliens.jpg). It’ll only work in Firefox (I’m using 45.0.1). Safari won’t allow feImage on non-SVG elements (as you noted in your Smashing Magazine article) and Chrome seems to have issues with applying multiple filters in sequence.

    https://nightjuggler.com/pie/?x&f=polar/blur-x,gb,8,1/depolar&c=660×600+0+0&i=http://w3.eleqtriq.com/wp-content/static/demos/2015/svg-translucency/img/aliens.jpg

    You can click the “Code” link to see the dynamically generated SVG. If you have a larger image, you can select an area to crop and drag the cropped area while the filters are applied. For example, this applies a polar filter on only the red and green channels of a 4080×2720 image. The unfiltered blue channel appears mostly yellow. You can drag the cropped area to change the region the filters are applied to. You can undo the crop by clicking Undo.

    https://nightjuggler.com/pie/?x&f=contrast,rgb,160/polar,rg&c=1500×1000+1450+1300

    Anyway, thanks for writing about cool things one can do with SVG!