When browsing the web with my retina iPad, I often see websites that could have used SVG for their cartoon-like graphics, but used PNG instead. It seems weird, because most likely those images have been created with some vector graphics editor and then exported or converted to bitmap images.
SVG has been supported in most browsers for years, but still it seems that developers are not yet comfortable with using SVG images on their websites.
SVG is quite well-supported in mobile browsers. This means that you can link to a SVG file on your page in most mobile browsers and it just works. But… there is one big problem: Android versions under 3 don’t have any kind of support for SVG in the stock browser. Desktop browsers have a similar situation with older Internet Explorer versions not supporting SVG.
Using SVG on older Android versions
Todd Anglin wrote a very good post on Kendo UI blog a year ago on how to deal with the situation on Android 2.X. In the blog post he describes how you can polyfill Android 2.X’s SVG support by using a javascript library to render SVG on a HTML5 canvas element.
In his post he writes that according to Google’s stats 94% of the Android users use version 2.1, 2.2 or 2.3. Fortunately the situation has improved a lot in a year, and that percentage is ~54% at the time of writing this article. Well, 54% of all Android users is still a huge amount of people!
Polyfilling missing SVG support with Canvas
You can use Modernizr and Canvg together to provide fallback(s) for SVG:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
This is just a basic example, so if you use it, you should add more logic to make the width
and height
values more flexible, and for example polyfill missing Canvas support or add a fallback to a PNG image.
Polyfilling SVG with Canvas demo
Here is a link to a JSBin example that shows the polyfilling in full action: http://jsbin.com/ujujuw/1/edit
Performance testing SVG to Canvas rendering
When I heard about the canvas rendering technique, I wanted to test if it is actually performant enough, knowing that SVG images might be a bit slow to render on slower mobile devices.
To get a good idea of the performance, the rendering speed needed to be tested with a really slow device. ZTE Blade was perfect for this kind of testing, having Android 2.2, 512Mb of RAM and a CPU with clock speed of only 600mhz. I ran some browser performance tests on it earlier, so I knew that it is slow.
For now I tested the polyfill performance with only one device, but I might revisit the testing with more 2.X devices when I have a bit of extra time.
For a test image I used this SVG image of map of Finland, that was rendered in 300x500 px resolution. I had an empty canvas element appended to body
element and used Canvg library to render the stringified SVG image data to the canvas element.
The test results were quite good for a device as slow as ZTE Blade. It would render the SVG map of Finland in 1.3 - 1.4 seconds. What the result means is that you should be able to use this technique to render at least 1-2 SVG images even on slower Android devices without making the user wait for too long.
Getting the SVG image out of Canvas
HTML5 Canvas element usually has a toDataUrl method that allows you to get the image data out of canvas as a data URL. Newer Android versions and iOS support this method, but unfortunately it does not work in Android 2.X stock browser.
I was looking for a workaround for this, and found a javascript library called todataurl-png-js that can be used to polyfill toDataUrl
method for PNG images on Android 2.X.
If you read the todataurl-png-js tutorial you might notice this:
It’s slow. There’s just no way a JS implemented method can keep up with a native one. Plus, the PNG format wasn’t created to be fast: it needs two checksums in order to create a working file and neither of these methods is implemented in a browser’s native code.
A quick performance test clearly demonstrates the awful speed of it.
It took 72 seconds on ZTE Blade to:
- First render the map of Finland to the canvas element
- Then to get the image data URL out of canvas with polyfilled
toDataUrl
method (this step took over 70 seconds) - Then to create an image with the data URL as source and append it to the test page.
Converting SVG images to @font-face icons
Since SVG on Android is more or less broken (unless you want to ignore over half of Android users), you can use @font-face icons to replace at least some of your SVG images. This is not a great workaround for the problem, because Android 2.1 stock browser, Windows Phone 7 IE9, Opera Mini and some other browsers do not support @font-face. @font-face also only works for SVG images that are “simple shapes”. You can’t use it to render complex images.
Rendering shapes or icons with @font-face has many limitations, but it is still a valid option in many cases.
– Using @font-face fonts has an additional benefit for desktop browsers: old Internet Explorer versions do not support SVG, but support @font-face fonts.
FontCustom
FontCustom is a command line tool that allows you to convert a bunch of SVG files to a @font-face icon font.
Installation on Mac OS X is easy, but you need to have Ruby (comes with OS X) and Homebrew installed first. Install FontCustom by running:
1 2 |
|
Put your SVG files inside a folder (mysvgfiles in this example) and run fontcustom compile
command:
1
|
|
…and your custom font is generated:
1 2 3 4 5 6 7 |
|
Converting SVG to @font-face demo
For the demo I took this silhouette of man SVG image by Nevit Dilme and converted it to a font icon.
When I set CSS font-size
to 1000px
, I could see some rendering issues with at least desktop Chrome and iOS Safari. Small part of the man’s pipe is cut off. I have not yet had time to investigate why it happens, and if it is something that can be easily fixed.
Take a look at this JSBin demo to see some very simple examples of font icon rendering and usage: http://jsbin.com/ijifev/2/edit
Minifying SVG image files
You might not be aware that the SVG images created by image editors contain a lot of extra data that you don’t need if you want to display the image on a web page.
SVG files, especially exported from various editors, usually contains a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.
– SVGO readme
You also might not be aware that your SVG images can be minified in a bit similar way as your javascript can be. If you minify your javascript, then why wouldn’t you do the same thing for your SVG images? A great tool to do it is called SVGO.
SVGO
To install it you need to have Node.js installed. SVGO can be installed by running:
1
|
|
Running the tool on the Finnish map SVG image gives quite impressive results by shaving off almost 40% of the original file size:
1
|
|
1 2 |
|
File size savings are of course smaller when the file is gzipped:
1 2 3 4 5 6 7 |
|
The gzipped size for the optimized image is only 11Kb, so it is not much bigger than a PNG equivalent would be. Compared to the PNG version, the SVG image looks sharp on retina screens and you can scale it as much as you want.
SVGO custom configuration file
You can also use a custom configuration file with SVGO (you can use the default configuration file as a template) where you are able disable and enable various compression settings. This is helpful if you want to make sure that the minification does not create any visual differences between the original and minified images.
1
|
|
Conclusion (tl;dr)
SVG is definitely used too little on websites despite of its good support on both desktop and mobile browsers.
The lack of SVG support in Android 2.X stock browser can be polyfilled (link) by rendering SVG to HTML5 Canvas element. The performance penalty of it does not seem to be as bad as I first thought.
Canvas element’s missing toDataUrl
method in Android 2.X stock browser makes it impossible to get the image out of the canvas element as a bitmap in a performant way. This means that if you don’t want to make the user wait for ages, you can only use the canvas element to show SVG images to the user.
Another workaround for Android 2.X’s lack of SVG support is to convert SVG images to @font-face icons. You can only render simple shapes or icons with it, and @font-face fonts do not work on Android 2.1 stock browser, Windows Phone 7 IE9, Opera Mini and some other browsers (link).
You should be using a minification tool like SVGO to possibly get noticeable file size savings on your SVG images.
Resources
David Bushell has written many good articles about SVG:
Chris Coyier also wrote about SVG:
SVG images can be blurry too, as Simurai points out:
Polyfilling SVG with Canvas:
Different ways of adding SVG to your page: