Over the past week I've been helping a friend experiment with SVG-based sprite sheets in the browser. We wanted to come up with an ideal workflow to prepare, publish, and run high-quality animated graphics in the browser. So ideally have a single source of animation data that would work for small smartphone screens, tablets, retina displays, and desktop browsers.
In theory, (vector-based) SVG should be ideal for that, but since SVG is not usually used that much - we decided to test it out. The idea was not to use SMIL SVG (so no SVG-based animation) but instead create an animation sprite-sheet (as you normally would with raster data PNG/JPG) but do this with pure vectors i.e. SVG. Its was a bit bigger but if it works this - it would work even with something thats even more optimized.
Plus frame-by-frame vector animation could do great things for our workflow - it would allow us to use the Flash editor to do the animations and then export them to SVG sprite sheets.
Anyway, the result works surprisingly well but also fails in some areas (please note for test purposes we only worked with Webkit-based browsers i.e. Safari, Chrome, mobile Safari on iOS, and Android ICS).
In CSS it's pretty easy to trigger hardware acceleration for a sprite sheet like this (at least in modern browsers with keyframes and steps) - you simply do this:
background-image: url(my.svg);
-webkit-animation: walk 1s steps(12, end) infinite;
to call keyframe-based animation shown here:
@-webkit-keyframes walk {
from { -webkit-transform: translate3d(0, 0, 0); }
to { -webkit-transform: translate3d(-100%, 0, 0); }
}
where the use of translate3d should let GPU kick in and the entire thing should be hardware accelerated in iOS mobile Safari and Android ICS browser.
Surprisingly enough considering that it's kind of a brute-force technique and a fairly large vector animation (600x600px for the test) - the whole thing flies. But its not perfect - it flickers in Safari before taking off. And in the ICS browser its flickering all the time so its not really usable.
So we tried the usual tricks to get rid of flickering such as:
-webkit-transform: translateZ(0);
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
But that didn't work. So then we tried rasterizing SVG dynamically in memory and using it as a texture with -webkit-transform: scale3d(1, 1, 0) but that didn't help ether.
Finally we just replaced the SVG with a rendered out PNG/JPG sprite sheet of the same size thinking the complex vectors are just too much for the browser - but guess what? Its the same issue - so its not SVG rendering at all - its a browser drawing issue. A further proof of that is if we slow the animation down to 1FPS - flickering still persists.
Is the image just too big for GPU? Have we reached the performance limit of what you're able to comfortably draw/animate in the browser (mobile in particular)?
I would really appreciate ideas/hacks on how to potentially get rid of the flickering (especially since its performing sooo fast). Its just a promising technique - hight performance browser animation that adapts to different screen sizes - the HTML5 Holy Grail ;)
With a few optimizations such as
<svg preserveAspectRatio="xMinYMax slice" viewBox="0 0 600 50">
and some CSS magic we're able to have the SVG adapt to its container perfectly and change its size from a single CSS class. It really would work wonders - but alas the flickering.
Anyway - please read more about it here where you're also able to try it out.
Pretty cool idea.
How about changing the zindex of the frames so you are layering the images on top of each other? This might solve the flickering because during the redraw the last frame is still visible. So, you'd just keep increasing the zindex value of the latest frame. Of course, there is a limit to that and you'll need to reset the zindex again, but it might have a big impact on cutting down the flickering.
I don't have ICS here to check it out, but on iOS 6 on an iPhone 5 and Jelly Bean 4.1.1 on a Galaxy Nexus the animation looks pretty smooth, except when I zoom, at which point I get a few flickery frames and then it settles down again.
Which reminded me of a similar issue I was having rendering onto a large canvas and transforming it around the screen (with the majority of the canvas offscreen at any given time).
My black-box analysis at the time suggested it was some optimization where the browser wasn't bothering to render offscreen content, instead waiting until it became visible, which completely defeated the purpose of having the large image and hardware-accelerating it around.
My "solution" was to -webkit-transform: scale the entire image down small enough to guarantee it all fits onscreen (so the browser has no choice but to render the entire image), let it render, and then scale it up to the size I wanted.
(As an aside: to hide this hackery from the user, I initially experimented with setting opacity: 0 so the pre-render wouldn't be visible, but it seems this had the same effect as offscreen rendering - it got optimized out. In the end I set the opacity very low and covered it with an almost opaque "loading" message, meaning the background rendering wasn't visible to the naked eye, but the browser had no absolute visible/invisibles to optimize out. Not sure if this was overkill, but it seemed to work for my setup.)
I'd be very interested to know if a similar technique might work for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With