Animating SVG: The Return.
A few months ago, I wrote a very small little note about animating SVGs — just some quick snippets that came to mind while I was working on a small SVG animation for a project. For the past few weeks, I’ve been working on a much more complex project involving SVGs and animation, so just wanted to write a mini case study about methods I tried and how they fared.
The basic setup: a blog-like site with an animated header and posts below it, responsive to desktop and tablet (but not phones). The posts can contain videos, animated GIFs, large photos, what have you, all of which open into a lightbox overlaid on the rest of the site. Since it’s like a blog, I implemented infinite scrolling, so there could be a whole lot of these content blocks — meaning scrolling performance is important to the whole thing. The header animation needs to work across the most devices and browsers without requiring user interaction, ruling out setting it as a background video or other such option that would need a user to hit “play” on a tablet. The animation itself consists of opacity changes for images within the SVG, so no crazy changes to actual point locations or shape sizes. The reason for using SVG here is that it allows for complex clipping paths and masking that you just can’t do with other methods. As far as other code, I’m already including Velocity.js to handle some other animations, so there’s no additional burden on loading by trying it out.
So I’ll walk through the various methods I tried and how they worked:
- Inline SVG with Velocity.js or CSS handling animations. This was the first method I tried just to get a good handle on all the animations and methods. It worked fairly well on Chrome but there were very quickly performance issues on Firefox and Safari — just choppy fading across the board. An additional downside was that the rest of the page was hurt by this — scrolling and lightbox animations stuttered and even video playback was like watching a flipbook at times. This was very clearly not going to work.
- Inline SVG with SMIL handling animations. Based on some excellent writing by Sara Soueidan over on CSS-Tricks, I decided to give SMIL animation a shot. It was immediately more performant in the header animation across the board while allowing me to chain animation start times. (Well, theoretically — this seemed to only be truly supported on Chrome, as Firefox required manual start times for some reason.) A big downside is that SMIL animation is not truly supported across the board — while it’s generally great in Chrome, Firefox, and Safari, it doesn’t work at all in Internet Explorer and different animations work differently with different syntax in different browsers (see: manual start times in Firefox). One method of “fixing” this to work is to include the FakeSmile polyfill that attempts to get SMIL working across all browsers. While I started including it here, I also very quickly shifted away from this method after some brief testing, as I was seeing the same performance issues as the first time — video choppiness, stuttery animations outside of the header, all that fun stuff.
- Embedding the SVG via an object tag with SMIL handling animations. I spent the most time on this method and was pretty sure this was the best way for a while. By using an object tag, I was able to load the SVG with all its animations and benefits while keeping it out of the DOM for performance. I immediately saw an increase in performance in scrolling, video playback, and other animations. I made sure to include the SMIL polyfill so that it would work across browsers, and while Firefox saw a tiny bit of choppiness in the header animation, it wasn’t enough to dissuade me. However, as soon as I pulled the page up on my iPad, I knew this wouldn’t work for long — the header animation was basically running at around five frames per second, scrolling wasn’t even working, and animations throughout the rest of the page were essentially nonexistent. Never mind testing video playback, I couldn’t even get Safari to avoid crashing long enough to see if it worked. Obviously, this was a no go.
- Embedding the SVG via an object tag with CSS handling animations. Almost identical to the last method, all I really did here was pull all of the SMIL animation (and references) out of the SVG file and wrote a bunch of CSS handling opacity changes via keyframes. On desktop, it worked fairly identically — nice performance, smooth animation, page was working great (in fact, I actually saw an increase in scrolling speed and other animations on the page). As soon as I pulled it up on iPad, it was like a whole different header, with smooth opacity changes and no problems with loading or choppiness. Scrolling was smooth, lightboxes came right into view without problem, and video playback worked about as well as video playback will as an embed. An added benefit was that I could use JavaScript to add or remove classes within the SVG itself, meaning I could pause CSS animations on scroll or lightbox and then resume them at will (a much smoother solution than the last round, where I was forcing them to display: none on scroll or lightbox in order to get around the performance hit). It seemed like we had a winner.
Obviously, this isn’t a thoroughly exhaustive list of methods and techniques — I certainly could’ve looked at Snap.svg or Raphaël or any of the other multitude of SVG libraries popping up, but none of the ones I saw seemed to fit my very specific use case (most of them seem very targeted toward vector shapes with no real inclusion of image masking and clipping). Hopefully, though, this can maybe help someone else down the line who is struggling to solve the problems of SVG animations with performance and interactions.
In the end, the biggest factor at play here was the user experience and ensuring visitor enjoyment of the site. By trying out a bunch of methods and techniques, I landed on the one that had the best possible combination of animation quality with overall performance — it may not be the perfect solution, but it’s the one that works best in the situation for both the designers/developers and visitors, and that’s what really matters.
This post was originally published on December 16, 2014.