Reputation: 190
To make SVGs responsive on our website with Internet Explorer 11 I am using the "Canvas Hack" by Nicholas Gallagher. This hack uses an extra canvas
element to make use the SVG keeps the aspect ratio. The whole structure of the inline SVGs looks something like this.
HTML:
<div style="position:relative;width:100%;">
<canvas width="256" height="256"></canvas>
<svg viewBox="0 0 256 256" preserveAspectRatio="xMaxYMax meet">
...
</svg>
</div>
CSS:
canvas {
display: block;
width: 100%;
visibility: hidden;
}
svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
I am using SVGInject to make the SVGs inline, which means the SVGs are kept is separate files. Before SVG injection the HTML looks like this:
<div style="position:relative;width:100%;">
<canvas width="256" height="256"></canvas>
<img src="myimage.svg" onload="SVGInject(this)" />
</div>
This works well, but maintenance is very annoying, because for each SVG the values of width
and height
for the canvas
must be manually set to match the SVG's aspect ratio. And since the SVGs are kept in separate files, I have to open the SVG every time to find out the aspect ratio.
So I was wondering, is this something that could automatically be done during injection?.
My ideas was to create a script that during injection somehow reads the SVG's aspect ratio from the viewBox
attribute and then set the width and height for the canvas accordingly.
Upvotes: 1
Views: 442
Reputation: 21
I had to deal with SVGs, created dynamically, so height is not known in advance. I found reference of the canvas hack here: Article about IE11 svg scaling hacks in German. For my solution, I wrote a simple little jQuery script to inject the proper canvas element, which is easy to read, adapt and reuse:
jQuery(function(){
if( jQuery( "svg.my" ).length > 0 ) {
var $svg_my = jQuery( "svg.my" );
for (var i=0; i<$svg_my.length; i++) {
// ----- viewBox calculation -----
var $svg_viewBox = jQuery($svg_my[i]).attr('viewBox');
$svg_viewBox = $svg_viewBox.replace(/\s\s+/g, ' ');
var $svg_width = $svg_viewBox.split(' ')[2]; // split() creates a string
var $svg_height = $svg_viewBox.split(' ')[3];
// ----- HTML - canvas fix -----
jQuery($svg_my[i]).wrap("<div class='svg_fix'></div>");
jQuery($svg_my[i]).parent().append("<canvas class='svg_fix' width='" + $svg_width + "' height='" + $svg_height + "'></canvas>");
jQuery($svg_my[i]).attr('width', '100%').attr('height', '100%');
}
}
});
svg.my {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
div.svg_fix {
position:relative;
margin-left:auto;
margin-right: auto;
}
canvas.svg_fix {
display: block;
width: 100%;
visibility: hidden;
}
All it does, is, wrapping the svg.my with a div element, and placing inside the div element, side by side with the svg.my, a dummie canvas element, and applying some simple CSS for styling. The canvas element ensures proper zoom and scaling in IE11. Therefore, it needs to be fitted with the width and height of the svg.my, accordingly.
This fix works with multiple svg.my per page. Now, sizing of each svg on the page can easily be achieved by changing the width of the associated canvas element.
Upvotes: 0
Reputation: 3533
SVGInject provides the following hooks to the injection: beforeLoad
, afterLoad
, beforeInject
and afterInject
.
In your case you can use afterInject
to modify the SVG, its parent, siblings, etc..
With using the afterInject
hook, you can not only set the width
and height
attributes of the <canvas>
element, but you can even check if Internet Explorer is running and only insert the canvas in that case. This will make your HTML much cleaner.
Here is a script (using jQuery) that will add the canvas only on Internet Explorer. In the <head>
add these lines (the one including svg-inject.js
should alrady be in your code):
<script src="svg-inject.js"></script>
<script>
SVGInject.setOptions({afterInject: function(img, svg) {
if (/Trident|MSIE/.test(window.navigator.userAgent)) { // if Internet Explorer
var $svg = $(svg);
// Get width and height from viewBox attribute if available
var viewBoxVals = ($svg.attr('viewBox') || '').split(/[\s,]+/);
var width = parseInt(viewBoxVals[2]);
var height = parseInt(viewBoxVals[3]);
if (width > 0 && height > 0) {
// Set position of parent div to relative
$svg.parent().css({position: 'relative'});
// Add canvas using width and height from viewBox
$svg.before('<canvas height="' + height + '" width="' + width + '"' +
'style="display: block; width: 100%; visibility: hidden;"></canvas>');
// Set SVG attributes to make it fill space reserved by canvas.
$svg.css({position: 'absolute', top: 0, left: 0});
}
}
}})
</script>
After the SVG is injected, the script checks if Internet Explorer is running. If so, it extracts the width
and height
values from the viewBox
attribute and inserts a canvas before the SVG. Also, the parent's and the SVG's attributes are set to make the SVG responsive.
SVGs can then be simply added like this:
<div style="width:100%;">
<img src="myimage.svg" onload="SVGInject(this)" />
</div>
No need to add the canvas in your HTML, it will be automatically inserted on Internet Explorer (and only on Internet Explorer).
Upvotes: 1