Code and Stuff

Jan 30, 2013

Animating SVG Filters applied to HTML elements

Animating CSS3 filters is possible. While the ones defined directly in CSS can be animated with CSS3 transitions, SVG filters require another approach. We can use SVG Animations. It is not as trivial as the transitions but still quite simple.

Setup

To animate SVG filters we simply have to put an animate tag as child of the element we want to animate. Animations can be timed, concatenate and even started with javascript.

Here an example on how to animate the intercept of all the RGB transfer functions of the feComponentTransfer filter from -1 to 1 and back repeatedly.

<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
  <filter id="myfilter">
    <feComponentTransfer>
      <feFuncR type="linear" slope="1" intercept="-1">
        <animate attributeName="intercept" attributeType="XML" 
            begin="0s" dur="4s" repeatCount="indefinite" 
            values="-1;1;-1"/>
      </feFuncR>
      <feFuncG type="linear" slope="1" intercept="-1">
        <animate attributeName="intercept" attributeType="XML" 
            begin="0s" dur="4s" repeatCount="indefinite" 
            values="-1;1;-1"/>
      </feFuncG>
      <feFuncB type="linear" slope="1" intercept="-1">
        <animate attributeName="intercept" attributeType="XML" 
            begin="0s" dur="4s" repeatCount="indefinite" 
            values="-1;1;-1"/>
      </feFuncB>
    </feComponentTransfer>
  </filter>
</svg>
As per usual the filter is applied to the dom element with some CSS:
<style type="text/css">
#someElement {
   filter: url(#myfilter);
   -webkit-filter: url(#myfilter);
}
</style>

Triggering animations on hover (Firefox)

Unfortunately setting the begin attribute to mouseover or click does not work. This probably because the filter is not applied to an SVG image but an HTML DOM element.

We will, therefore, need some Javascript to simulate a CSS3 Transition for an SVG filter. Looks like there is some problems with SVG filter animations in chrome, so this only works properly with firefox.

JQuery has the very handy hover method to add the two event handlers. As implemented the hover transitions must be very short (0.5s or so). A cleaner implementation should check the actual position of the one animation to start the other. This would allow smoother transition with no jumps when the transition happens before the previous one ends.

$(function() { // on DOM ready register the events
  $("#theItem").hover(function(event){
    // stop the out event
    $("#myfilter animate").get(1).endElement();
    // start the in one
    $("#myfilter animate").get(0).beginElement();
  }, function(event){
    $("#myfilter animate").get(0).endElement();
    $("#myfilter animate").get(1).beginElement();
  });
});
The filter will need this time two animation with undefined begin and end times. An animated saturation filter would look like this:
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
  <filter id="myfilter">
    <feColorMatrix type="saturate" values="0">
      <animate attributeName="values" attributeType="XML" 
                  begin="indefinite" end="indefinite" dur="0.5s" 
                  fill="freeze" from="0" to="1"/>
      <animate attributeName="values" attributeType="XML" 
                  begin="indefinite" end="indefinite" dur="0.5s" 
                  fill="freeze" from="1" to="0"/>
    </feColorMatrix>
  </filter>
</svg>
On firefox moving the mouse over the image should add color to it.

Chrome Vs. Firefox

With CSS3 SVG Filters firefox seams to work better. It appears to implement more stuff. I found three things where Chrome appears to have some problems:
  • Animating some of the filters, for instance the ColorMatrix's values attribute (but I'm not sure if it has to)
  • Support for some common attributes of SVG filters (result, X, Y, Width, Height)
  • Strangely, when called while handling some specific DOM event, the beginElement function will run the animation only the first time

No comments: