Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 204
basement Avatar asked Aug 18 '17 17:08

basement


People also ask

How do I scale SVG viewBox?

Just set the viewBox on your <svg> , and set one of height or width to auto . The browser will adjust it so that the overall aspect ratio matches the viewBox . Beautiful.

How do I zoom in viewBox?

Use viewBox for zooming But you can also use it to zoom. For example, for an image with dimensions of 630 x 630, the full image is shown if viewBox is 0 0 630 630 . To zoom to the bottom right, use 315 315 315 315 (the top-left point is the middle of the image, and the width/height is half the original).

Is viewBox required for SVG?

viewbox is like a second set of virtual coordinates – all vectors inside the SVG will use the viewbox, while you can manipulate the actual height, width properties of the SVG without affecting the inside,. An SVG with a viewBox is so much easier to work with. I would never put an SVG together without one.

Can you change SVG viewBox with CSS?

In order to change the value of the SVG viewport's width and height, we can use CSS. Simple. But to change the value of the viewBox , we currently need to use JavaScript. Not all SVG presentation attributes are available as CSS properties; only the set of attributes that have CSS property equivalents can be set in CSS.


2 Answers

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>
like image 159
Paul LeBeau Avatar answered Nov 05 '22 12:11

Paul LeBeau


Since the code snippet expired and I had to give it some thought on how to implement it I thought I would spare others from doing it and post a working code snippet.

var svg = document.querySelector('svg');
var viewBox = svg.viewBox.baseVal;

function zoomIn(){
    viewBox.x = viewBox.x + viewBox.width / 4;
    viewBox.y = viewBox.y + viewBox.height / 4;
    viewBox.width = viewBox.width / 2;
    viewBox.height = viewBox.height / 2;
}

function zoomOut(){
    viewBox.x = viewBox.x - viewBox.width / 2;
    viewBox.y = viewBox.y - viewBox.height / 2;
    viewBox.width = viewBox.width * 2;
    viewBox.height = viewBox.height * 2;
}

If you implement a panning feature it will work with it too without any modifications.

like image 37
LaszloR1 Avatar answered Nov 05 '22 11:11

LaszloR1