Jamie S
Jamie S

Reputation: 2299

How to scale SVG elements separately.

I am making a graph in D3, and I am trying to scale the Y axis of the graph on window resize, without scaling the size of the elements within it. (And I don't want to have to redraw the graph).

To say this another way, I want to be able to use SVG to scale the distance between objects, without changing the size of the objects.

Reading the last part of this article and seems to suggest I can, but I can't seem to get the details right.

Here is an simplified codepen of what I am trying to do: https://codepen.io/thesuperuser/pen/BmdNwz/

<symbol id="circle" width="20" height="20" preserveAspectRatio="none">
  <circle cx="10" cy="10" r=10></circle>
</symbol>

and then I use this symbol, and try and preserve the size of the symbol with d3

let svg2 = d3.select('svg.scaled')
svg2.selectAll('use')
  .data(data)
  .enter()
  .append('use')
  .attrs({
    class: 'circle',
    href: '#circle2',
    x: d => scale(d),
    y: '50%',
    height: dotR*2,
    width: dotR*2,
  })

This seems to just duplicate the behavior in the first example, which scales both size and distance the way you would expect viewBox and preserveAspectRatio to behave.

Upvotes: 1

Views: 1097

Answers (1)

ccprog
ccprog

Reputation: 21811

If you set a viewBox for the svg element, it will scale that viewBox including all content to fit the viewport. What you want is the opposite: a svg element where content positioning can vary, but nothing gets scaled. The key to this is relative (percentage) units.

Here is a working pattern:

<svg width="100%" height="200">
  <symbol viewBox="0 0 20 20" preserveAspectRatio="xMidYMid meet">
    <circle id="circle" cx="10" cy="10" r="10" />
  </symbol>
  <use xlink:href="#circle" x="5%" y="50%" width="10%" height="20" />
  <use xlink:href="#circle" x="15%" y="50%" width="10%" height="20" />
  ...
</svg>

This way, when the width of the SVG changes, the viewports of the use elements change their width, but not their height. The circles rendered inside them fit the constant height (as long as the viewports are wider than high). preserveAspectRatio="xMid..." and the viewport x and width result in the circle being centered at 10%, 20%, ... Change these to position the circles as needed.

Here's a codepen to demonstrate with d3.

Upvotes: 1

Related Questions