SVG filter effects apply graphic operations to images and elements in nondestructive ways. You can add filters to any SVG element or text and even to bitmapped images. Because SVG filters are nondestructive the original image is never changed and you can return to it by removing any filters you’ve added.
Last week I started a series on SVG filters and I said SVG filters are composed of three parts, a filter element which contains one or more filter primitives that perform a graphic operation like blurring an image and finally a filter property that references the filter element and applies it to an image or graphic element.
Today I want to talk about the filter element and the filter property. In the following weeks I’ll talk about filter primitives and their input and output. Then I’ll walk you through each of the different filter primitives SVG provides with details and examples.
The Filter Property
Let’s start with the filter property, since it doesn’t need a lot of explanation. If you remember the example from the last post, I created a square using the <rect> element and referenced a filter on it.
<rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#blur)" />
The filter=“url(#blur)” part is the reference and this is the filter property. There’s not much to it. Just note the syntax. The value takes the form url() and inside the parenthesis you list the id of the filter you want to add, in this case a filter with an id of blur.
You can add the filter property to any SVG element as well as SVG container elements (aside from the mask element).
That’s all there is to the filter property, however I should note the filter property is not the only way to use a filter. You can also use the <use> element to reference a filter, which you might choose for filters that create their own elements or reference external images.
The Filter Element
The filter element is a container for filter primitives which do the actual work of modifying the display of an image or graphic element.
The filter element is never directly rendered. It’s defined on its own and then used when referenced by the filter property of an SVG element or container. Because it’s defined in one place and referenced in another, it’s common, but not necessary, to wrap the filter element inside the <defs> element. I’ll follow the convention throughout the series.
If the filter is referenced by a container element, such as a group <g>, the filter will apply to all the children inside the container. Otherwise it applies only to the element that referenced it.
Aside from some common attributes, such as id, that any SVG element can accept, the filter element can accept the following attributes and their possible values.
- y — <coordinate>
- width — <length>
- height — <length>
- filterUnits — userSpaceOnUse | objectBoundingBox
- primitiveUnits — userSpaceOnUse | objectBoundingBox
- xlink:href — <iri>
All of these attributes might look familiar. It seems like every SVG element takes coordinates and lengths and if you’ve done any work with SVG, you’ve likely encountered different kinds of units and here are two more filterUnits and primitiveUnits.
To explain these and the other attributes as they apply to the filter element we need to talk a little about the filter effects region.
The Filter Effects Region
The filter effects region is a region on the SVG canvas where a given filter applies. It doesn’t have to be the same region occupied by the element referencing the filter. Most of the attributes on the filter element define the filter effects region.
I think an example will help make this clearer. In the example from last week I defined a filter with a single filter primitive that added a Gaussian blur. The filter had an id of blur, which was referenced by the second of two rectangles using the filter property.
That’s the review part. What I’ve added since last week are the x, y, width, and height attributes to the filter element.
1 2 3 4 5 6 7 8 9 10
<svg width="100%" height="220" style="outline: 1px solid red"> <defs> <filter id="blur" x="-10%" y="-10%" width="120%" height="120%"> <feGaussianBlur stdDeviation="3" /> </filter> </defs> <rect x="10" y="10" width="100" height="100" fill="#00f" /> <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#blur)" /> </svg>
Here’s the result and if you remember last week’s example you might notice the result here looks exactly the same. That’s because I used the default values for each attribute so this example ends up being the same as not having including them.
The values of x, y, width, and height create a region of space, inside of which, the filter effect applies. In this example that area starts 10% to the left and above the rectangle and it ends 10% to the right and below the rectangle.
Let’s see what happens if we change one of values. Here I changed the value of y from –10% to 0%. Notice that the square on the right no longer has a blurry top edge. That’s because the filter, which used to start 10% above this top edge, now starts exactly where the top edge starts.
What happens if I change y to 10%? Now the top 10% of the square is gone.
Hopefully you get the idea. Similar things happen if we change x, width, or height. The initial values of –10% and 120% ensure the effect applies to an area 10% larger than the element being filtered. Any changes to these values modifies the location and dimensions of the affected region.
The boundaries of the filter effects region act like a hard clipping area, which is why the top 10% of the square in the second example disappeared. It’s outside the region and gets clipped.
Be aware that the filter effect itself might extend outside the boundary of the element referencing it. That’s the case with the Gaussian blur, which is why a value of 0 for y clipped the effect, even though it didn’t clip the square.
Because of the potential to clip the element that takes the filter, you might think it best to create a larger filter effects region than you think you’ll need. However, the amount of memory and processing time required to apply a filter are related to the size of the filter effects region so a larger region could be a performance hit.
As you can tell from the examples x and y can be negative numbers. That’s not true for width and height which must be positive. You might also have noticed that I set all four (x, y, width, and height) using percents. That’s because of the next attribute, filterUnits.
The filterUnits attribute defines the coordinate system for x, y, width, and height. It accepts one of two values userSpaceOnUse or objectBoundingBox.
- userSpaceOnUse—represents values in the current user coordinate system in place at the time when the filter is referenced
- objectBoundingBox — represents fractions or percentages of the bounding box on the referencing element
The latter is the default, which is why I used percents in the previous example. Here’s the example again with the filterUnits set to userSpaceOnUse and the values for x, y, width, and height adjusted.
1 2 3 4 5 6 7 8 9 10
<svg width="100%" height="220" style="outline: 1px solid red"> <defs> <filter id="blur4" x="105" y="0" width="120px" height="120px" filterUnits="userSpaceOnUse"> <feGaussianBlur stdDeviation="3" /> </filter> </defs> <rect x="10" y="10" width="100" height="100" fill="#00f" /> <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#blur4)" /> </svg>
I set the filter effects region so it would be 10px larger than the square all around. The top left corner of the square is located 115px from the left edge of the viewport and 10px from the top leading to the values of 105px and 0px for x and y. Since the square is 100px by 100px, values of 120px each for width and height extend the filter effects region 10px past the edge of the square on the right and bottom.
Here’s the result, which should look like the result of the earlier example.
The Filter Primitive Subregion
The primitiveUnits attribute is similar to filterUnits, but as you might guess it affects the filter primitives instead of the filter. The attribute is still added to the filter element and it takes the same userSpaceOnUse or objectBoundingBox values.
The default value is userSpaceOnUse, which is the opposite of the filterUnits attribute. This means the default filter primitive subregion is the same size as the default filter effects region. You can, of course, change the default.
Here I changed the primitiveUnits to objectBoundingBox and then set the subregion to fall an extra 10% outside of the element.
1 2 3 4 5 6 7 8 9 10
<svg width="100%" height="220" style="outline: 1px solid red"> <defs> <filter id="blur5" primitiveUnits="objectBoundingBox"> <feGaussianBlur stdDeviation="0.033" x="-10%" y="-10%" width="120%" height="120%" /> </filter> </defs> <rect x="10" y="10" width="100" height="100" fill="#00f" /> <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#blur5)" /> </svg>
You may have noticed I changed the stdDeviation of the feGaussianBlur from 3 to 0.33. That was to accommodate the new subregion units, which you’ll also note was set by adding x, y, width, and height to the primitive itself.
As with the filter effects region, the filter primitive subregion acts as a hard clipping area. Here’s the previous example with a change in the height from 120% to 60%. You can see the bottom of the second square has been clipped.
The remaining attribute, xlink:href, is a way to refer to another filter element within the current SVG fragment. However, I’ve yet to get this attribute to work directly on the filter element and I’ve yet to find an example anywhere where it’s used this way.
The xlink:href attribute does work on other elements like <use> and <image> and it will also work on the filter primitive <feImage>, which I’ll get to in a few weeks, but not directly on the <filter> element itself as far as I can tell. If you know of a working example, please point me to it.
There are two things to take away from this post. First is the idea that the filter element is a container for filter primitives and it’s the primitives that perform the graphic operation.
Second is the idea of the filter effects region and filter primitive subregion, which you define using the x, y, width, and height attributes added to the filter element or the filter primitives inside it.
Both act like a hard clipping path so you want to make sure they’re large enough to include the filter effect you want to apply, but not any larger as it will take up more processing power to apply the filter over a larger area.
You can also change the coordinate system through the filterUnits and/or primitiveUnits attributes so either region can be relative to the viewport or relative to the element being filtered.
I have a few more things to say about filter primitives, specifically their input and output and how the output of one primitive can be used as the input of another. That will be the topic for next time.
Download a free sample from my book, Design Fundamentals.