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>
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.