zaarr78
zaarr78

Reputation: 477

SVG Scale group from his center

I'm trying to scale an <g></g> called #RectangleScaled by a 1.4 factor from the group center. I'm using this formula

translate(-centerX*(factor-1), -centerY*(factor-1)) scale(factor)

But the red rectangle doesn't seem to have the right translation

Here is my implementation

var rectangle = document.querySelector("#Rectangle")
var rectangleScaled = document.querySelector("#RectangleScaled")

var canvasBbox = document.querySelector("#Canvas").getBBox()

var x = -canvasBbox.width/2*(1.4-1)
var y = -canvasBbox.height/2*(1.4-1)

rectangleScaled.style.transform = "translate("+x+"px, "+y+"px) scale(1.4)"

Svg

<svg width="411" height="731" viewBox="0 0 411 731" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g id="Canvas" transform="translate(721 384)">
    <clipPath id="clip-0" clip-rule="evenodd">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>
    </clipPath>
    <g id="Google Pixel" clip-path="url(#clip-0)">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>

      <g id="RectangleScaled">
        <use xlink:href="#path1_fill" transform="translate(-614 -152)" fill="red"/>
      </g>
      <g id="Rectangle">
        <use xlink:href="#path0_fill" transform="translate(-614 -152)" fill="#C4C4C4"/>
      </g>
    </g>
  </g>
  <defs>
    <path id="path0_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
    <path id="path1_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
  </defs>
</svg>

Here is the fiddle

Upvotes: 2

Views: 6763

Answers (3)

var rectangle = document.querySelector("#Rectangle")
var rectangleScaled = document.querySelector("#RectangleScaled")

var canvasBbox = document.querySelector("#Canvas").getBBox()
var s = 1.4; // you can change this value to scale
var cx = canvasBbox.x + canvasBbox.width/2;
var cy = canvasBbox.y + canvasBbox.height/2;

var x = (cx * -(s - 1)) * 1;
var y = (cy * -(s - 1)) * (10 * s);

rectangleScaled.style.transform = "translate("+x+"px, "+y+"px) scale("+s+")"
svg {
  border:solid;
}
<svg width="411" height="731" viewBox="0 0 411 731" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g id="Canvas" transform="translate(721 384)">
    <clipPath id="clip-0" clip-rule="evenodd">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>
    </clipPath>
    <g id="Google Pixel" clip-path="url(#clip-0)">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="yellow"/>
      
      <g id="RectangleScaled">
        <use xlink:href="#path1_fill" transform="translate(-620 -360)" fill="red"/>
      </g>
      <g id="Rectangle">
        <use xlink:href="#path0_fill" transform="translate(-620 -360)" fill="#C4C4C477"/>
      </g>
    </g>
  </g>
  <defs>
    <path id="path0_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
    <path id="path1_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
  </defs>
</svg>

Upvotes: 0

Paul LeBeau
Paul LeBeau

Reputation: 101800

You appear to have got your centre wrong. You are scaling around the wrong centre point.

You are dividing canvasBBox width and height by two, but you are not taking into account the x and y values of the bbox.

The centre of the #Canvas element is at

var cx = canvasBbox.x + canvasBbox.width/2;
var cy = canvasBbox.y + canvasBbox.height/2;

If you update the calculation with these values, you get your expected result.

var rectangle = document.querySelector("#Rectangle")
var rectangleScaled = document.querySelector("#RectangleScaled")

var canvasBbox = document.querySelector("#Canvas").getBBox()

var cx = canvasBbox.x + canvasBbox.width/2;
var cy = canvasBbox.y + canvasBbox.height/2;

var x = -cx * (1.4 - 1)
var y = -cy * (1.4 - 1)

rectangleScaled.style.transform = "translate("+x+"px, "+y+"px) scale(1.4)"
<svg width="411" height="731" viewBox="0 0 411 731" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g id="Canvas" transform="translate(721 384)">
    <clipPath id="clip-0" clip-rule="evenodd">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>
    </clipPath>
    <g id="Google Pixel" clip-path="url(#clip-0)">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>

      <g id="RectangleScaled">
        <use xlink:href="#path1_fill" transform="translate(-614 -152)" fill="red"/>
      </g>
      <g id="Rectangle">
        <use xlink:href="#path0_fill" transform="translate(-614 -152)" fill="#C4C4C4"/>
      </g>
    </g>
  </g>
  <defs>
    <path id="path0_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
    <path id="path1_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
  </defs>
</svg>

Upvotes: 4

mpro
mpro

Reputation: 15040

Only for Chrome (consider it as a bug): For scale and keep the scaled element in the center position you can do it with pure CSS, by adding transform-origin: center; style to #RectangleScaled selector.

var rectangle = document.querySelector("#Rectangle")
var rectangleScaled = document.querySelector("#RectangleScaled")

var canvasBbox = document.querySelector("#Canvas").getBBox()


rectangleScaled.style.transform = "scale(1.4)"
svg {
  border:solid;
}

#RectangleScaled {
  transform-origin: center;
}
<svg width="411" height="731" viewBox="0 0 411 731" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <g id="Canvas" transform="translate(721 384)">
    <clipPath id="clip-0" clip-rule="evenodd">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>
    </clipPath>
    <g id="Google Pixel" clip-path="url(#clip-0)">
      <path d="M -721 -384L -310 -384L -310 347L -721 347L -721 -384Z" fill="#FFFFFF"/>
      
      <g id="RectangleScaled">
        <use xlink:href="#path1_fill" transform="translate(-614 -152)" fill="red"/>
      </g>
      <g id="Rectangle">
        <use xlink:href="#path0_fill" transform="translate(-614 -152)" fill="#C4C4C4"/>
      </g>
    </g>
  </g>
  <defs>
    <path id="path0_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
    <path id="path1_fill" d="M 0 0L 196 0L 196 266L 0 266L 0 0Z"/>
  </defs>
</svg>

Upvotes: 0

Related Questions