86

I have tried many variants of the svg parameters, but have had no joy in scaling this particular SVG.

I am trying to contain this SVG into a container element that controls the size of the SVG.

I'm aiming for 500x309px.

What combination of width, height, viewBox and preserveAspectRatio can help me achieve this without having the SVG masked or cropped?

Phrogz
  • 296,393
  • 112
  • 651
  • 745
sgb
  • 2,234
  • 2
  • 19
  • 31
  • I assume that you do not want non-uniform scaling: you do not want the map to stretch or squash based on the container. If the container is 1000x100 you want the map 100 px tall; if 100x1000 you want it 100px wide. Correct? – Phrogz Mar 05 '12 at 20:16

3 Answers3

144
  1. You absolutely must have a viewBox attribute on your SVG element that describes the bounding box of the contents you want to be always visible. (The file that you link to does not; you'll want to add one.)

  2. To cause your SVG to fill an HTML element, put the CSS attribute position:relative (or position:absolute or position:fixed if appropriate) on your wrapper, and then

  3. Set the CSS attribute position:absolute on your <svg> element to cause it to sit inside and fill your div. (If necessary, also apply left:0; top:0; width:100%; height:100%.)

Once you have a viewBox and your SVG sized correctly the default value of the preserveAspectRatio attribute will do what you want. In particular, the default of xMidYMid meet means that:

  • The aspect ratio described by your viewBox will be preserved when rendering.
    By comparison, a value of none would allow non-uniform scaling.
  • The viewBox will always meet either top/bottom or left/right, with 'letterboxing' keeping the other dimension inside.
    By comparison, a value of slice ensures that your viewBox fully fills the rendering, with either the top/bottom or left/right falling outside the SVG.
  • The viewBox will be kept vertically and horizontally centered within the SVG viewport.
    By comparison, a value of xMinYMax would keep it in the bottom-left corner, with padding only to the right or top.

You can see this live here: http://jsfiddle.net/Jq3gy/2/

Try specifying explicit values for preserveAspectRatio on the <svg> element and press "Update" to see how they affect the rendering.

Edit: I've created a simplified version of the US Map with a viewBox (almost half the bytes) and used that in an updated demo to suit your exact needs: http://jsfiddle.net/Jq3gy/5/

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • Just to clarify my doubt, to fill the svg to the containing div, you have asked to specify position absolute to the svg. But the svg expands both in height and width, without specifing the position absolute on svg or position relative on container div.(checked by modifying the eclipis example) – Rajkamal Subramanian Mar 13 '12 at 09:36
  • 1
    Just awesome. Using that with the US map applies directly to what I want to work with. Thanks for your insight. – mikevoermans Feb 26 '13 at 20:44
  • A note on debugging: If this isn't working in modern browsers, the most likely reason is you've accidentally set the viewBox dimensions incorrectly, either badly from memory or some SVG optimizers strip it. – Dominic Woodman Jun 19 '16 at 15:59
  • What am I missing here? On every browser where I try this fiddle, as the height shrinks, the SVG just gets clipped, rather than resizing to fit the new height. (However, if I put width/height 100% on the SVG, it scales in both directions.) – JW. Feb 09 '20 at 00:31
  • This does not seem to work (anymore). While the animation is running on the examples, it's fine. As soon as they stop, the SVG breaks out through the bottom (on Chrome) – nuts Oct 03 '21 at 09:33
10

Set the SVG width and height to be the size of its container, and set preserveAspectRatio = none.

<div height="50" width="100">
  <svg preserveAspectRatio="none" viewBox="0 0 300 200"></svg>
</div>

and

$("svg").each(function(){
  this.width = this.parentNode.width;
  this.height = this.parentNode.height;
}

That's it. Setting CSS is not needed.

I personally set viewBox to be the size of the contents of the SVG. So in my example, the original image I am loading into my SVG is 300x200. It will shrink to fit a 50x100 div. But viewBox manipulation is a separate issue.

john k
  • 6,268
  • 4
  • 55
  • 59
2

Unfortunately, I don't know the answer that applies to raw SVG, but in Raphael.js, I did it like that:

var paper = Raphael('#container', your_container_width, your_container_height);
paper.setViewBox(realSvgWidth, realSvgHeight, true);

This technique scaled my SVG to fit the bounds.

Hope this helps.

Valentin V
  • 24,971
  • 33
  • 103
  • 152