SVG Drop Shadows
A look at trying to animate drop shadows in SVG to support a material design style
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.js"></script>
<h1>Material Design in SVG</h1>
<p>This JSFiddle is a result of my investigation into replicating material design aspects within SVG. The aim is to produce an affect whereby a circle appears to be lifting off the surface it is currently on, closer to the screen. The best way to produce the affect is 2 fold, increasing the size of the circle, and changing the shadow. Most of these examples focus just on the latter, as that seems to be the most difficult bit. It should be noted that in my use case I have lots of circles, and only want a subset of them to lift up.</p>
<h2>CSS Keyframes</h2>
<p>CSS Keyframes perform really nicely and give a great affect. The biggest problem is that I've been unable to get the animation working on a single circle. The SVG, G and Circle elements all have ID's. But applying the keyframe animation to anything other than the the SVG element as a whole doesn't work. Therefore you end up with all circles having a transitioning shadow.</p>
<svg id="svg_keyframes" width="1000" height="280">
<g id="svg_keyFrames_Group" transform="translate(120, 140)">
<circle id="svg_keyFrames_Circle" r="20"/>
</g>
</svg>
<h2>CSS Keyframes referencing SVG Filters</h2>
<p>This approach takes the CSS Keyframes a bit further by specifying the frames in 25% increments.
<ul>
<li>The first circle attempts to do this by changing the SVG filter that is being referenced, unfortunately this simply doesn't work.</li>
<li>The middle circle proves that standard CSS filters for fill effects works as you would expect.</li>
<li>The last circle attempts to do something similar applying drop-shadow incremenets for the keyframes the first 'CSS Keyframes' example, but again this doesn't work.</li>
</ul></p>
<svg id="svg_filterKeyFrames" width="1000" height="280">
<defs>
<filter id="f1" x="-40%" y="-40%" height="200%" width="200%">
...
CSS
circle {
fill: #cccccc;
}
text {
fill: white;
}
/*****************/
/* CSS KeyFrames */
/*****************/
#svg_keyframes{
animation:filters 700ms infinite;
}
@-webkit-keyframes filters {
0%{
-webkit-filter:drop-shadow(0px 0px 2px red);
}
50% {
-webkit-filter:drop-shadow(0px 0px 15px red);
}
100% {
-webkit-filter:drop-shadow(0px 0px 0px red);
}
}
/***********************************/
/* CSS KeyFrames using SVG Filters */
/***********************************/
.kf_Shadow1 {
-webkit-animation-name: shadow-expand; / Chrome, Safari, Opera /
-webkit-animation-duration: 2s; / Chrome, Safari, Opera /
-webkit-animation-iteration-count: infinite;
animation-name: shadow-expand;
animation-duration: 2s;
animation-iteration-count: infinite;
}
.kf_Fill1 {
-webkit-animation-name: circle-fill; / Chrome, Safari, Opera /
-webkit-animation-duration: 2s; / Chrome, Safari, Opera /
-webkit-animation-iteration-count: infinite;
animation-name: circle-fill;
animation-duration: 2s;
animation-iteration-count: infinite;
}
.kf_DropShadow1 {
-webkit-animation-name: drop-shadow-expand; / Chrome, Safari, Opera /
-webkit-animation-duration: 2s; / Chrome, Safari, Opera /
-webkit-animation-iteration-count: infinite;
animation-name: drop-shadow-expand;
animation-duration: 2s;
animation-iteration-count: infinite;
}
/* Demonstrate that fill works correctly */
@keyframes circle-fill {
0% { fill: #FF0000; }
25% { fill: #BB0033; }
50% { fill: #990066; }
75% { fill: #4400aa; }
100% { fill: #0000ff; }
}
/* Demonstrate that filter doesn't work as hoped */
@keyframes shadow-expand {
0% { filter: url(#f1); -webkit-filter: url(#f1);}
25% { filter: url(#f2); -webkit-filter: url(#f1);}
50% { filter: url(#f3); -webkit-filter: url(#f1);}
75% { filter: url(#f4); -webkit-filter: url(#f1);}
...
JavaScript
/*************************/
/* JavaScript Animations */
/*************************/
(function() {
var svg = d3.select("#svg_javaScriptAnimation");
setInterval(function() {
// Animate
svg.selectAll(".circle")
.transition()
.duration(1950)
.attr("r", 110);
svg.select("#shadowImg")
.transition()
.duration(1950)
.ease("linear")
.attrTween("xlink:href", function() {
var interpolate = d3.interpolate(1, 5);
return function(t) {
return 'https://dl.dropboxusercontent.com/u/41796243/JSFiddle/SVG%20Filters/'+ Math.floor(interpolate(t)) + '.png';
};
});
svg.selectAll(".jA_shadow")
.transition()
.duration(1950)
.attr("r", 130);
svg.selectAll(".jA_shadow_expanding")
.transition()
.duration(1950)
.attr("r", 110);
svg.selectAll(".jA_shadow_large")
.transition()
.duration(1950)
.attr("r", 110);
svg.selectAll(".jA_manual_shadow")
.transition()
.duration(1950)
.attr("r", 110);
svg.selectAll(".jA_shadow_filter")
.transition()
.duration(1950)
.styleTween("filter", function() {
var interpolate = d3.interpolate(10, 31);
return function(t) {
return 'url(#f'+ Math.floor(interpolate(t)) + ')';
};
});
// Reset
svg.selectAll(".circle")
.transition()
.delay(1960)
.duration(1)
.attr("r", 110);
svg.selectAll(".jA_shadow")
.transition()
.delay(1960)
.duration(1)
.attr("r", 110);
svg.selectAll(".jA_shadow_expanding")
.transition()
...