Moving ViewBox X Does Not Position SVG Predictably

37 views Asked by At

I have an SVG on my page that is the full height of the the viewport, and wider than the viewport. It's very panoramic. I have an HTML element that is centered on my page on mobile, and like right 25% on desktop. (I saw this just to illustrate that viewBox values cannot be statically set).

I am trying to "pan" the SVG (via viewBox) so that the center of a certain path element is aligned with the center of the HTML element. This works perfectly well when just the x viewBox property is modified:

<svg viewBox="0 0 1000 240">/* svg code */</svg>
const SVG_INITIAL_VIEWBOX_HEIGHT = 240
const htmlElToCenterAgainst = document.querySelector('#centerAgainst')
const svgPathEl = document.querySelector('#path')

const svgPathElRect = svgPathEl.getBoundingClientRect()
const svgPathElCenter = svgPathElRect.left + svgPathElRect.width / 2

const htmlElToCenterAgainstRect = htmlElToCenterAgainst.getBoundingClientRect()
const htmlElToCenterAgainstCenter = htmlElToCenterAgainstRect.left + htmlElToCenterAgainstRect.width / 2

const viewportToSvgRatio = viewportHeight / SVG_INITIAL_VIEWBOX_HEIGHT // use the ratio of viewbox pixels to screen pixels to normalize calculations

const distanceToTargetCenter = svgPathElCenter - htmlElToCenterAgainstCenter

const viewBoxX = distanceToTargetCenter / viewportToSvgRatio

const viewBox = `${viewBoxX} 0 1000 ${SVG_INITIAL_VIEWBOX_HEIGHT}`

The problem is when I try to achieve a zoom by increasing the viewBox height. For example a zoom of 0.5x:

const viewBox = `${viewBoxX} 0 1000 ${SVG_INITIAL_VIEWBOX_HEIGHT * 2}`

This correctly zooms out the viewBox. And of course its left edge has shifted left, because my centering calculation is not longer valid with the new zoom level. The SVG path is now to the left of the object I'm centering against.

I can position SVG path with its center aligned with the page left edge:

const svgPathCenterAtLeftEdge = ((svgPathElRect.x + (svgPathElRect.width / 2)) / viewportToSvgRatio)
const viewBox = `${svgPathCenterAtLeftEdge} 0 1000 ${SVG_INITIAL_VIEWBOX_HEIGHT * 2}`

And I can calculate the HTML element's center:

const htmlElCenter = htmlElToCenterAgainstRect.x + htmlElToCenterAgainstRect.width / 2

Therefore, to move the SVG path element into alignment to the HTML element, it seems like I should just add the htmlElCenter to the viewBox's x. This moves it too far left by a long way.

So it occurs to me that the SVG "viewBox pixels" and "screen pixels" are not the same thing. I've heard it described that with viewBox, a new grid is defined. So perhaps I need to reduce it by half, since that is how I have "zoomed in" using viewBox.

const viewBox = `${svgPathCenterAtLeftEdge + (htmlElCenter / 2)} 0 1000 ${SVG_INITIAL_VIEWBOX_HEIGHT * 2}`

That pans the SVG in the right direction, but not nearly enough.

I have tried to modify this x value using both the viewBox "scale" and the viewportToSvgRatio I calculated above. However, no variation seems to pan the SVG into alignment with the HTML element. I must be missing piece of the viewBox grids/pixel definition. Or maybe SVG itself.

How can I use viewBox to align this SVG element with an HTML element (when the viewBox height has been modified)?

0

There are 0 answers