SVG Targeted Zooming

by louisbros

HTML

<div>
    <svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.2">
        <circle cx="200" cy="200" r="100" fill="red" stroke="blue" stroke-width="10" />
    </svg>
</div>

CSS

svg {
    outline: 1px solid red;
    width: 400px;
    height: 400px;
}

JavaScript

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

function createPoint(x, y) {
    return {
        x: x,
        y: y
    };
}

function calculateTarget(diff, centerPoint, mousePoint) {
    return createPoint(
        diff / 2 * (mousePoint.x / centerPoint.x) * -1,
        diff / 2 * (mousePoint.y / centerPoint.y) * -1
    );
}

function calculateZoom(svg, diff, mousePoint) {
    const svgRect = svg.getBoundingClientRect(),
        centerPoint = createPoint(svgRect.width / 2, svgRect.height / 2),
        viewRect = svg.viewBox.baseVal,
        vw = viewRect.width,
        vh = viewRect.height,
        vx = viewRect.x,
        vy = viewRect.y,
        diffPoint = calculateTarget(diff, centerPoint, mousePoint);

    return {
        x: vx + diffPoint.x,
        y: vy + diffPoint.y,
        width: vw + diff,
        height: vh + diff
    };
}

function zoom(svg, diff, mousePoint) {
    const viewBoxZoom = calculateZoom(svg, diff, mousePoint);
    svg.setAttribute('viewBox', `${viewBoxZoom.x} ${viewBoxZoom.y} ${viewBoxZoom.width} ${viewBoxZoom.height}`);
}

svg.addEventListener('mousewheel', (event) => {
    event.preventDefault();
    zoom(svg, event.deltaY > 0 ? 10 : -10, createPoint(event.offsetX, event.offsetY));
});