Question

I'm developing a small educational tool, for which I'd like to add drop-shadows to SVG paths. To construct my graphics I use the D3.js. This usually works lovely, but with specific shapes I run into problems, especially with small shapes that have horizontal and/or vertical lines.

To illustrate this, I made a JSFiddle example with three triangles. The shadow on the green triangle looks pretty, the shadow on the orange triangle in still alright, but the shadow on the red triangle looks very ugly.

The shadow is created using this code as an example:

var defs = svg.append("defs").attr("height","160%");
var filter = defs.append("filter").attr("id", "schaduw");
filter.append("feGaussianBlur").attr("in", "SourceAlpha")
      .attr("stdDeviation", 5).attr("result", "blur");
filter.append("feOffset").attr("in", "blur")
      .attr("dx", 3)
      .attr("dy", 3)
      .attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode").attr("in", "offsetBlur");
feMerge.append("feMergeNode").attr("in", "SourceGraphic");

As can be seen in the JSFiddle code, the only meaningful difference between the shapes is their size. How can I prevent the small shapes from having ugly shadows?

Any help is very much appreciated! =)

Was it helpful?

Solution 2

The quality of the shadow that you refer to is controlled by the standard deviation of the Gaussian blur you use. Using an absolute value there means that the shadow is "spread out" the same amount regardless of the size of the element you're using the filter on. For smaller shapes, this has the effect you're seeing.

There are two ways of fixing this. The quick and easy way is to decrease the standard deviation such that it looks good even for the smallest shapes. I've done this in your example here by setting .attr("stdDeviation", 1).

The other option is to have different standard deviations for different shape sizes as I've done here in a hacky way. This would be much more difficult because it would require you to compute the size of a shape, determine a suitable standard deviation and potentially create an appropriate filter if it doesn't exist. Also, the shadows look less consistent.

OTHER TIPS

Your shadows are being clipped by the filter region of the underlying SVG Filter. By default filter effects are shown in a 10% overflow region around the filtered shape. Your drop shadows on the smaller shapes are too big and are overflowing that region. This is causing the edges. You need to add an explicit filter region and make it bigger. This will expand the filter region to a 20% region surrounding the shape:

var filter = defs.append("filter").attr("id", "schaduw") .attr("width", 1.4) 
    .attr("height",1.4) .attr("x",-0.2) .attr("y",-0.2);

If you want a shadow that is proportional to the filtered element, you do not need to compute the size of anything. SVG has a built-in ability to do relative sizing by defining your units in objectBoundingBox units. Here youshould use objectBoundingBox units as your filter primitive units, not the (silent) default of userSpaceUnits ("regular" viewbox units). You need to restate the stdDeviation of the blur, as well as the offset of the offset in %'s of the originating element box expressed as a decimal:

var filter = defs.append("filter").attr("id", "schaduw") .attr("width", 1.4) 
     .attr("height",1.4) .attr("x",-0.2) .attr("y",-0.2)     
     .attr("primitiveUnits","objectBoundingBox");
filter.append("feGaussianBlur").attr("in", "SourceAlpha")
              .attr("stdDeviation", .02).attr("result", "blur");
filter.append("feOffset").attr("in", "blur")
              .attr("dx", .01)
              .attr("dy", .01)
              .attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode").attr("in", "offsetBlur");
feMerge.append("feMergeNode").attr("in", "SourceGraphic");

Now, you'll notice that this looks a little weird - because as long as you want all shapes to look like they're on the same plane, your drop shadow size should be consistent across shapes no matter how small or large they are.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top