Skip to content

Instantly share code, notes, and snippets.

@veltman
Last active February 8, 2021 12:27
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save veltman/5cd1ba0b3c623e7b5146 to your computer and use it in GitHub Desktop.
Save veltman/5cd1ba0b3c623e7b5146 to your computer and use it in GitHub Desktop.
Responsive SVG with sticky text

Keeping text size sticky while making an SVG responsive with viewBox.

  1. Set the SVG's viewBox to its original width and height, and its width to 100%, so it fills its container.
  2. On resize, find out the SVG's new width.
  3. Update the SVG's height to preserve the aspect ratio.
  4. Use transform to scale up the text by the inverse. For example, if the new width is 2/3 of the original, make the text 3/2 scale.

Instead of keeping the text size fixed, you could constrain it to some other range so that it shrinks but more slowly than the rest of the SVG. For example:

var textScaleFactor = d3.scale.linear()
  .domain([300,600]) // expected limits of SVG width
  .range([1.5,1]); // when SVG is 1/2 width, text will be 2/3 size

See also: Hannah Fairfield's connected scatterplot on gas prices

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.14/d3.min.js"></script>
<script>
var width = 960,
height = 440;
var container = d3.select("body").append("div")
.style("width",width);
d3.text("gas.svg",function(err,raw){
container.html(raw);
var svg = d3.select("svg")
.attr("width","100%")
.attr("height",height)
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", "0 0 " + width + " " + height);
var text = svg.selectAll("text");
var grid = svg.selectAll("line");
simulateResize();
// Simulate a window resize
// In real life you would skip this and use e.g.:
// window.onresize = resized;
function simulateResize(expand) {
container.transition()
.duration(4000)
.style("width",(expand ? width : width / 1.5) + "px")
.tween("simulate",function(){
return resized;
})
.each("end",function(){
simulateResize(!expand);
});
}
function resized() {
var scale = width / container.node().getBoundingClientRect().width;
// Update SVG height to maintain aspect ratio
svg.attr("height",height / scale);
// Fix grid size at 1px too
grid.style("stroke-width",scale + "px");
// Increase text scale proportional to overall scale reduction
// e.g. 3/4 of the original width -> scale text by 4/3
text.attr("transform","scale(" + scale + " " + scale + ")");
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment