Last active
February 3, 2016 21:56
-
-
Save shawnbot/a09c69d0f4eb70190a95 to your computer and use it in GitHub Desktop.
SVG viewBox and intrinsic dimensions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Responsive SVG elements</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
html { | |
font-family: "Helvetica Neue", Helvetica, arial, sans-serif; | |
font-size: 16px; | |
} | |
body { | |
margin: 0; | |
padding: 1rem; | |
} | |
svg { | |
display: block; | |
width: 100%; | |
height: auto; | |
} | |
p { | |
margin: 0 0 1rem 0; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<p id="hooray" style="display: none"> | |
<b>Hooray!</b> Your browser uses the viewBox attribute's aspect ratio | |
to calculate SVG elements' intrinsic dimensions. | |
</p> | |
<div id="boo" style="display: none"> | |
<p> | |
<b>Boo.</b> Your browser doesn't use the viewBox attribute's aspect | |
ratio to calculate SVG elements' intrinsic dimensions. But <i>fear not!</i> | |
</p> | |
</div> | |
<svg id="circles" viewBox="0 0 300 100"> | |
<circle r="50" cx="50" cy="50" fill="cyan"></circle> | |
<circle r="50" cx="150" cy="50" fill="magenta"></circle> | |
<circle r="50" cx="250" cy="50" fill="yellow"></circle> | |
</svg> | |
<svg id="circles" viewBox="0 0 200 200"> | |
<circle r="50" opacity=".5" cx="50" cy="50" fill="red"></circle> | |
<circle r="50" opacity=".5" cx="100" cy="100" fill="green"></circle> | |
<circle r="50" opacity=".5" cx="150" cy="150" fill="blue"></circle> | |
</svg> | |
</div> | |
</body> | |
<script> | |
(function() { | |
// show the appropriate message | |
if (obeysViewBox()) { | |
d3.select("#hooray").style("display", null); | |
return; // exit if the browser does what we expect it to | |
} else { | |
d3.select("#boo").style("display", null); | |
} | |
/* | |
* Select all of the SVG elements on the page and parse out their viewBox | |
* attributes to determine their aspect ratios. | |
*/ | |
var svg = d3.selectAll("svg") | |
.datum(function() { | |
var viewBox = this.getAttribute("viewBox").split(" "), | |
size = viewBox.slice(2), | |
width = size[0], | |
height = size[1], | |
aspect = width / height; | |
return { | |
viewBox: viewBox, | |
width: width, | |
height: height, | |
aspect: aspect | |
}; | |
}) | |
.call(resize); | |
/* | |
* Whenever the window resizes, resize all of the SVG elements. | |
* You could debounce this method so that it happens less frequently, but | |
* it should be fast as-is. | |
*/ | |
window.addEventListener("resize", function() { | |
svg.call(resize); | |
}); | |
/* | |
* a simple test for whether or not the browser uses an <svg> element's | |
* viewBox aspect ratio to calculate its intrinsic dimensions, just like an | |
* <img> element. All we need to do is create an SVG and give it a fixed | |
* width and auto height, then measure the height to see if its aspect ratio | |
* matches that of the viewBox dimensions. | |
*/ | |
function obeysViewBox() { | |
var test = d3.select("body").append("svg") | |
.attr("viewBox", "0 0 10 50") | |
.style("width", "200px") | |
.style("height", "auto"); | |
var rect = test.node().getBoundingClientRect(); | |
test.remove(); | |
return rect.height === 1000; | |
} | |
/* | |
* Resize one or more SVG elements using their calculated width and aspect | |
* ratio, as cacluated from their viewBox attribute. This assumes that | |
* elements have a variable width. Variable height SVG elements would set the | |
* "width" style to `Math.ceil(rect.height * d.aspect) + "px"`. | |
*/ | |
function resize(selection) { | |
selection.style("height", function(d) { | |
this.style.height = "auto"; | |
var rect = this.getBoundingClientRect(), | |
height = rect.width / d.aspect; | |
return isFinite(height) | |
? Math.ceil(height) + "px" | |
: null; | |
}); | |
} | |
})(this); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment