htmlcsssvgzoomingviewbox

How to keep viewBox centered when "zooming" in SVGs?


Often I use the viewBox attribute to "zoom" a SVG element. Zoom is accomplished of course by using lower width and height values for the viewBox attribute - but the x and y values are tricky to figure out for keeping the scene centered.

Below is a snippet where viewBox="0 0 100 100". ( x y width height ) This is my starting point. The scene is to scale and starts at the top left corner.

<head>
  <link rel="stylesheet" href="https://codepen.io/basement/pen/oepKxY.css">
</head>

<body> 
  <iframe src="https://s.codepen.io/basement/debug/zdpVyV/PNAvYLZmJRQr"
          id="grid"
  ></iframe>

  <div class="wrp">   
  
    <!-- SVG relevant code below-->
    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="0 0 100 100"
         width="100" height="100"
         class="canvas"
    > 
      <defs>   
        <style type="text/css"> 

          circle {
            fill: #0ff;
            fill-opacity: 0.25;
            stroke-width: 0.25;
            stroke: #0ee;
          }

        </style>       
      </defs>

      <circle cx="37.5" cy="50" r="25" />
      <circle cx="62.5" cy="50" r="25" />
      <circle cx="50" cy="71.65" r="25" />    
    </svg>
    
  </div>
</body>

( Viewable in full page. Snippets are responsive )

Zooming in

Changing the width and height values to 50 in the viewBox zooms the scene. However I had to adjust the x and y values both to half the viewBox dimensions: 25 to keep the drawing centered.

viewBox="25 25 50 50"

<head>
  <link rel="stylesheet" href="https://codepen.io/basement/pen/oepKxY.css">
</head>

<body> 
  <iframe src="https://s.codepen.io/basement/debug/zdpVyV/PNAvYLZmJRQr"
          id="grid"
  ></iframe>

  <div class="wrp">   
  
    <!-- SVG relevant code below-->
    <svg xmlns="http://www.w3.org/2000/svg"
         viewBox="25 25 50 50"
         width="100" height="100"
         class="canvas"
    > 
      <defs>   
        <style type="text/css"> 

          circle {
            fill: #0ff;
            fill-opacity: 0.25;
            stroke-width: 0.25;
            stroke: #0ee;
          }

        </style>       
      </defs>

      <circle cx="37.5" cy="50" r="25" />
      <circle cx="62.5" cy="50" r="25" />
      <circle cx="50" cy="71.65" r="25" />    
    </svg>
    
  </div>
</body>

Halving the width and height isn't consistent

Following the pattern of halving the width and height to center the scene:

viewBox="12.5 12.5 25 25" should continue the zoom and stay centered. But it doesn't stay centered. My question is what formula or technique does SVG use that I can always figure out mathematically what x and y value I need to center a specific width and height value in the viewBox?

Note: I'm aware of libraries like Snap.svg and Raphaël. I'm avoiding libraries in an effort to understand the fundamentals.

One more thing: I'm asking if you already have a width and height value in the viewBox how do you find the center coordinates of the scene. Not vice versa.

External stylesheets were included for visual cognition. The only relevant code to this question is pertaining to the SVG elements.


Solution

  • Your calculations are wrong. If the width and height of the viewBox get smaller, the x and y values need to get bigger.

    Initial viewBox:  0 0 100 100
    Zoom X2:          25 25 50 50
    Zoom X4:          37.5 37.5 25 25
    

    To get the x or y values, you need to subtract half the new width or height from the halfway point of the last (or original) viewBox.

    Zoom X2:  centre - newWidth/2
              = (100/2) - (50/2) = 25
    Zoom X4:  (100/2) - (25/2) = 37.5, or
              (25 + 50/2) - (25/2) = 37.5
    

    Another way to calculate it is to subtract the current width from the original width and divide by two.

    Zoom X4:  (100 - 25) / 2 = 37.5
    

    <head>
      <link rel="stylesheet" href="https://codepen.io/basement/pen/oepKxY.css">
    </head>
    
    <body> 
      <iframe src="https://s.codepen.io/basement/debug/zdpVyV/PNAvYLZmJRQr"
              id="grid"
      ></iframe>
    
      <div class="wrp">   
      
        <!-- SVG relevant code below-->
        <svg xmlns="http://www.w3.org/2000/svg"
             viewBox="37.5 37.5 25 25"
             width="100" height="100"
             class="canvas"
        > 
          <defs>   
            <style type="text/css"> 
    
              circle {
                fill: #0ff;
                fill-opacity: 0.25;
                stroke-width: 0.25;
                stroke: #0ee;
              }
    
            </style>       
          </defs>
    
          <circle cx="37.5" cy="50" r="25" />
          <circle cx="62.5" cy="50" r="25" />
          <circle cx="50" cy="71.65" r="25" />    
        </svg>
        
      </div>
    </body>