11.2 Creating a Drop Shadow
Establishing the Filter's Bounds
-
The <filter> element has attributes that describe the clipping region for a filter.
You specify an x, y, width, and height in terms of the percentage of the filtered object's bounding box. (That is the default.)
Any portion of the resulting output that is outside the bounds will not be displayed. - If you are intending to apply a filter to many objects, you may want to omit these attributes altogether and take the default values of
x="-10%",y="-10%","width=120%",height="120%".
This gives extra space for filters — such as the drop shadow that we're constructing—that produce output larger than their input. - These attributes are in terms of the filtered object's bounding box; specifically,
filterUnitshas a value ofobjectBoundingBoxby default. . - If you wish to specify boundaries in user units, then set the attribute's value to userSpaceOnUse
Example 1.The first attempt to produce a drop shadow on the flower, using the <feGaussianBlur> filter primitive.
-
in="SourceAlpha"as its input (the in attribute), stdDeviation="2" - the amount of blur with the attribute.
The larger this number, the greater the blur.
If you give two numbers separated by whitespace as the value for stdDeviation,
for example
stdDeviation="2 5"
-the first number is taken as the blur in the x-direction,
-the second is taken as the blur in the y-direction.
<svg width="400" height="400" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000">
<filter id="drop-shadow">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="1"/>
</filter>
<use xlink:href="svg_es_11_0_filters_flower.svg#flower" filter="url(#drop-shadow)"/>
</svg> Don't be surprised;
remember, the filter returns the output, which is a blurred alpha channel,
instead of the original source graphic.
You can get the effect you want by adding #flower transferred by some vector.
Example 2. Second attempt by adding transferred #flower
<svg width="400" height="400" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000">
<filter id="drop-shadow">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="1"/>
</filter>
<use xlink:href="svg_es_11_0_filters_flower.svg#flower" filter="url(#drop-shadow)"/>
<use xlink:href="svg_es_11_0_filters_flower.svg#flower" transform="translate(-2,-2)"/>
</svg>
Storing, Chaining, and Merging Filter Results
Example 3. Improved drop shadow filter
<svg width="400" height="400"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000">
<filter id="drop-shadow">
<feGaussianBlur
in="SourceAlpha"
stdDeviation="1"
result="blur"/>
<feOffset
in="blur"
dx="2" dy="2"
result="offsetBlur"/>
<feMerge>
<feMergeNode in="offsetBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<use xlink:href="svg_es_11_0_filters_flower.svg#flower"
filter="url(#drop-shadow)"/>
</svg>
- The
resultattribute specifies that the result of this primitive can be referenced later by the nameblur.
This isn't like an XML id; the name you give is a local name that's valid only for the duration of the primitives contained in the current<filter>. - The
<feOffset>primitive takes its input, in this case the blur result from the Gaussian blur, offsets it by the specifieddxanddyvalues, and stores the resulting bitmap under the nameoffsetBlur - The
<feMerge>primitive encloses a list of<feMergeNode>elements, each of which specifies an input. The inputs are stacked one on top of another in the order that they appear. In this case, you want the offsetBlur below the original SourceGraphic.
Claude Opus 4.5 - Why the method in Example 3 is better than the method in Example 2 - Demonstration
Demo A: Reusability - Apply shadow to multiple objects with one filter definition
Method 2 (two <use> per object) - 6 elements needed:
<filter id="demo-shadow-2">
<feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
</filter>
<circle cx="30" cy="40" r="15" fill="gray" filter="url(#demo-shadow-2)"/>
<circle cx="28" cy="38" r="15" fill="red"/>
Method 3 (feMerge) - 3 elements needed:
<filter id="demo-shadow-3">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"/>
<feOffset in="blur" dx="2" dy="2" result="offsetBlur"/>
<feMerge>
<feMergeNode in="offsetBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<circle cx="30" cy="40" r="15" fill="red" filter="url(#demo-shadow-3)"/>
Demo B: Animation - Shadow follows automatically with Method 3
Method 2 - Shadow does NOT follow (broken):
<circle cx="25" cy="25" r="12" fill="gray"
filter="url(#anim-shadow-2)"/>
<circle cx="25" cy="25" r="12" fill="orange">
<animate attributeName="cx"
values="25;125;25"
dur="3s" repeatCount="indefinite"/>
</circle>
Method 3 - Shadow follows correctly:
<circle cx="25" cy="25" r="12" fill="orange"
filter="url(#anim-shadow-3)">
<animate attributeName="cx"
values="25;125;25" dur="3s"
repeatCount="indefinite"/>
</circle>