CSS3 filter: drop-shadow spread property alternatives
Well, I figured out how to replace the non-functioning spread property using SVG filters. Big thanks to Michael Mullany though his answer wasn't 100% what I need. Here's the filter I'm using:
<filter id="drop-shadow">
<feMorphology operator="dilate" radius="6" in="SourceAlpha" result="dilated"/>
<feGaussianBlur stdDeviation="3" in="dilated" result="blurred"/>
<feFlood flood-color="rgba(255,255,255,0.5)" in="blurred" result="flooded"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
feMorphology dilate operator replicates the functionality I wanted very nicely, making it easier to give the text a 'glow' effect that conforms a lot more strictly to the outline of the text.
(Oddly, feFlood does nothing and I'm unable to get a white glow, but that's a problem for another question. The filter also eats up 100% of a single core as long as it's open in a tab in the latest Chrome. Oh well.)
Why doesn't spread radius property for `filter: drop-shadow(...);` work?
This MDN page is a bit misleading.
While it might come to the specs one day, they currently state that
Note: Spread values or multiple shadows are not accepted for this level of the specification.
To put it in context, they say so because the drop-shadow
filter takes the same parameters has the box-shadow
one, which does have a spread value.
Spreading a box-shadow
is an easy thing to do, but drop-shadow
filter can apply on far more complex shapes, so it's harder to implement.
Even SVG filters don't have an equivalent, though it might be possible to make something there, with a lot of work.
To the defense of that MDN article, it has a warning box stating
Most browsers do not support this parameter; the effect will not render if used.
Ps: here is an svg filter generator I made, approximating that. It's not entirely equivalent to what box-shadow's spread does, but it might be enough for some cases.
const filtered = document.getElementById( 'filtered' );const box_shadow = document.getElementById( 'box_shadow' );const clipped = document.getElementById( 'clipped' );inp.onchange = function() { filtered.style.borderRadius = this.value; box_shadow.style.borderRadius = this.value;};inp.onchange();
filtered.style.filter = clipped.style.filter = 'url( #' + spreadingBoxShadow( 20, 20, 10, 50, "blue" ) + ')';
function spreadingBoxShadow( offset_x, offset_y, blur_radius, spread_radius, color ) {
const _id = "spread-radius-" + Date.now(); const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.innerHTML = ` <filter id="${ _id }" x="-100%" y="-100%" width="300%" height="300%" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feFlood flood-color="${ color }" result="flood" in="SourceAlpha" /> <feComposite in2="SourceAlpha" in="flood" operator="atop" result="color" /> <feMorphology operator="dilate" radius="${ spread_radius}" result="spread" in="color"/> <feGaussianBlur in="spread" stdDeviation="${ blur_radius }" result="shadow"/> <feOffset dx="${ offset_x }" dy="${ offset_y }" in="shadow" result="offset"/> <feMerge result="merge"> <feMergeNode in="offset"/> <feMergeNode in="SourceGraphic"/> </feMerge> </filter>`; document.body.append( svg ); return _id;
}
#box_shadow,#filtered,#clipped > div { width: 325px; height: 200px; background: green; margin: 30px 50px 100px;}#box_shadow { box-shadow: 20px 20px 10px 50px blue;}#clipped { padding: 50px;}#clipped > div { clip-path: polygon(50% 0, 70% 10%, 70% 80%, 0 40%);}.cont { position: relative;}
Change border-radius here: <input id="inp" value="120px 200px / 100px 0px"><div class="cont"> Filter:<br> <div id="filtered"></div> Box-shadow:<br> <div id="box_shadow"></div> Clipped:<br> <div id="clipped"> <div></div> </div></div>
Get drop-shadow to spread further
There's no direct alternative to the spread property, which isn't actually part of the standard. It may be possible to use SVG Filters as per CSS3 filter: drop-shadow spread property alternatives. Otherwise it may be possible to use one image on top of the other and use transforms to grow the background, but I'm not exactly sure if that's the effect you're going for.
@keyframes logo-grow {
0% {
transform: scale(1);
}
100% {
transform: scale(1.3);
}
}
Then apply that to the background image. You'd need to make sure the two images match up exactly in terms of sizing or the overlap would look bad (like in the demo above).
CSS3 filter:opacity(X) with fallback to opacity:X, same for filter:drop-shadow() to box-shadow
The support for filter
(with and without prefix) should overlap pretty nicely with support for the Conditional Rules module (@supports
), with the exception of older versions of Safari/iOS (before Safari 9). If you treat it as an enhancement (i.e. with a fallback to regular opacity
for other browsers), that shouldn't be a big issue. Try something like this, perhaps?
.half-transparent {
opacity: 0.5;
}
@supports ((filter: opacity(0.5)) or (-webkit-filter: opacity(0.5))) {
.half-opacity {
opacity: 1;
-webkit-filter: opacity(0.5);
filter: opacity(0.5);
}
}
Why does box-shadow look different than filter: drop-shadow
I believe this is a bug. The W3C specification for CSS filters states that "values are interpreted as for box-shadow [CSS3BG]." Therefore, similar results should be expected from the two properties.
I achieved a similar issue, as seen here:
#box1, #box2 { position: absolute; top: 10px; width: 100px; height: 100px; background: red;}
#box1 { /* Using drop shadow, should appear identical to box shadow */ left: 10px; filter: drop-shadow(0 5px 10px black)}
#box2 { left: 120px; box-shadow: 0 5px 10px black;}
<div id="box1"></div><div id="box2"></div>
How to have a drop shadow on a transparent rect svg
You can't do this if the original is a fully transparent shape - because of reasons - but you can do this starting from an almost completely transparent original shape and end up with a fully transparent shape surrounded by a normal drop shadow.
Draw your shapes with 1% fill-opacity. When you pull those into a filter, multiply their alpha by 100 using a colormatrix - and use that as the basis for your dropshadow. You won't end up using the original 1% opacity shape in your final version because if you use the "out" operator - this discards the contents of anything that overlaps with the original (processed) shape.
svg { background: #33D; }
<svg width="500px" height="400px"><defs> <filter id="trans-shadow"> <feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 0" result="boostedInput"/> <feGaussianBlur stdDeviation="5"/> <feComposite operator="out" in2="boostedInput"/> </filter></defs>
<circle filter="url(#trans-shadow)" x="100" y="100" r="050" cx="150" cy="150" fill="black" fill-opacity="0.01" />
</svg>
Related Topics
Page-Specific CSS with Rails App
Trouble Having CSS Order (Flex Box) Work in an Email
How to Span to the Last Column in an Implicit Grid
Position Floated Elements Directly Under Each Other
Css: Adding a Border Changes the Background-Color (!)
IE7 Absolute Element Appearing Behind Relative One
How to Change the Bootstrap 4 Range Slider Colors
How to Repeat a CSS Grid Layout Pattern
CSS Two Columns with Known Children Width
Responsive Two Gradient Diagonal Button
Columns in Bootstrap 3.0 Only Stacking Vertically
Calculating Width from Percent to Pixel Then Minus by Pixel in Less CSS
Font Face Isn't Working in Iis 8.0