Search code examples
javascriptsvgsvg.js

Scaling and repositioning with SVG.js difficulties


I’m using svg.js to manipulate SVGs within the browser. Most of what I’m doing is relatively simple, but I’m having some difficulties with scaling/positioning a few objects.

I have an SVG that contains a “Pin” icon. You can click on the SVG to zoom it (which just resizes it to fill the browser viewport), in turn resizing all of its children, including the Pin. I need to scale this icon back down to its original size of 36px x 36px and reposition it so the bottom center of the Pin sits where it originally sat. I’ve got the resizing down, but the repositioning piece escapes me.

Some example states:

  1. Scaled at 100% with Pin at base size of 36px x 36px.

    enter image description here

  2. Scaled up by 9.77241379 with pin scaled down to its base size of 36px x 36px. Using svg.js the scale() method scales at the center point of the pin, leaving it floating in space.

    enter image description here

What I’m using to scale the Pin when the parent container is scaled up:

scaleHotspot(hotspot) {
    const child = hotspot.first();
    const bbox = child.bbox();
    const rbox = child.rbox();
    const scale = bbox.w / rbox.w;

    hotspot.scale(scale);
}

Because the Pin is scaled using its center point it’s now sitting up higher than it’s supposed to be. I need to determine how much to move the Pin down to have the point of the Pin sitting in its original position.

I originally thought this worked but testing in various places yielded strange results.

const newY = -(bbox.height / 2 - (bbox.height - (1 / scale)));

Suggestions around getting the Pin positioned so that its bottom center point sits where the unscaled version was?


Solution

  • With .transform(), you can define a center around which to scale. Use .bbox() to find that point in current user space. Since these coordinates already include pre-existing transformations, set the relative flag to add the scaling on top:

    scaleHotspot(hotspot) {
        const bbox = hotspot.bbox();
        const rbox = hotspot.rbox();
        const scale = bbox.w / rbox.w;
        const center = bbox.x + bbox.w / 2;
        const bottom = bbox.y + bbox.h;
    
        hotspot.transform({scale: scale, cx: center, cy: bottom}, true);
    }