martin.p
martin.p

Reputation: 363

How to zoom and scroll an <svg> element

I'm a newbie in html/css/JS development, I'm trying to learn for hobbyist purpose. I'm trying to create a page with an <svg> element inside a <div> and I'm looking for a way to pan with scrollbar and zoom its content without using external libraries (because I'd like to understand the mechanism behind first).

Here below the code of my page with a couple of buttons to zoom in and out a blue square drawn with a <rect> element. More or less it works but when I'm zooming in and scroll bars appear, when I drag them I'm not able to see completely the square, it looks clipped out if the viewport.

I also tried to play with the viewBox attribute in the but probably I didn't fully got how it works, maybe that's the right way to go....

How could I modify this code to obtain the result I'm looking for in a robust and elegant way?

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0px;
            padding: 0px;
        }

        #container {
            width: 80lvw;
            height: 80lvh;
            border: 1px solid black;
            justify-content:left;
            overflow: auto;
            --scale-k: 1;
        }

        #svg {
            transform: scale(var(--scale-k));
            transform-origin: center;
        }
    </style>

    <script>

    </script>
</head>

<body>
    <div id="container">
        <svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" height="100%" width="100%" stroke="black"
            stroke-width="3" fill="transparent">
            <rect x="100" y="100" width="100" height="100" fill="blue" rx="20" ry="20" />
        </svg>
    </div>
    <button type="button" id="zoom-in" style="z-index: 100000">zoom in</button>
    <button type="button" id="zoom-out" style="z-index: 100000">zoom out</button>
</body>

<script>

    const container = document.querySelector('#container');
    const svg = document.querySelector('#svg');

    let zoomF = window.getComputedStyle(container).getPropertyValue('--scale-k');
    console.log(zoomF);

    const btnZoomIn = document.querySelector('#zoom-in');
    const btnZoomOut = document.querySelector('#zoom-out');

    btnZoomIn.addEventListener('click', (evt) => {
        zoomF *= 1.1;
        resize();
    });

    btnZoomOut.addEventListener('click', (evt) => {
        zoomF /= 1.1;
        resize();
    });

    function resize() {
        let svgWidth = parseInt(svg.getAttribute('width'));
        svg.setAttribute('width', `${(svgWidth * zoomF)}%`);
        let svgHeight = parseInt(svg.getAttribute('height'));
        svg.setAttribute('height', `${(svgHeight * zoomF)}%`);
        container.style.setProperty('--scale-k', zoomF);
    }


</script>

</html>

Upvotes: 2

Views: 3089

Answers (1)

dean gl&#252;kler
dean gl&#252;kler

Reputation: 193

Here's my take on it. There are quite a few changes so take a moment to compare to your version.

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
         * {
           margin: 0px;
           padding: 0px;
         }
         #container {
           width: 80lvw;
           height: 80lvh;
           border: 1px solid black;
           overflow: auto;
         }
         #svg {
           margin: 50px;
         }
      </style>
   </head>
   <body>
      <div id="container">
         <svg width="100" height="100" id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" 
            fill="transparent">
            <rect stroke-width="3" stroke="black" width="100%" height="100%" fill="blue" rx="20" />
         </svg>
      </div>
      <button id="zoom-in">zoom in</button>
      <button id="zoom-out">zoom out</button>
   </body>
   <script>
      const svg = document.querySelector('#svg');
      
      const btnZoomIn = document.querySelector('#zoom-in');
      const btnZoomOut = document.querySelector('#zoom-out');
      
      btnZoomIn.addEventListener('click', () => {
          resize(1.1);
      });
      
      btnZoomOut.addEventListener('click', () => {
          resize(0.9);
      });
      
      function resize(scale) {
          let svgWidth = parseInt(svg.getAttribute('width'));
          svg.setAttribute('width', `${(svgWidth * scale)}`);
          let svgHeight = parseInt(svg.getAttribute('height'));
          svg.setAttribute('height', `${(svgHeight * scale)}`);
      }
   </script>
</html>

I notice that this solution has a little render issue with the border radius but I'm only concerned with the zooming for now.

Upvotes: 2

Related Questions