Ucodia
Ucodia

Reputation: 7700

SVG transform-origin not working in Chrome

I have a SVG based app that makes heavy use of transformation such as translates, rotates and scales. While I have no issue in Firefox, in Chrome, the transform-origin property is not taken in account. It seems to apply the user-agent default value 0px 0px 0.

Here is an example (JSFiddle):

<svg width="400" height="400">
  <defs>
    <rect id="shape" width="200" height="200"/>
  </defs>
  <g transform="translate(100,100)">
    <use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
    <ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
    <g transform="translate(0,0) scale(0.5) rotate(45)" style="transform-origin: 100px 100px;">
      <use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
    </g>
  </g>
</svg>

transform-origin issue

As you can see Chrome applies all transformation from top left corner of the shape regardless of the defined origin while Firefox respects the defined origin.

Am I missing something about how transform-origin works with SVG?

Does anyone actually found a way to fix this without compensating with translates?

Upvotes: 7

Views: 12427

Answers (3)

Winona
Winona

Reputation: 81

The magic Word is: transform-box: fill-box; Put it to the and see the magic happens. Now it will behave as an html-object.

Upvotes: 8

Ucodia
Ucodia

Reputation: 7700

I am answering to my own question in order to clarify entirely what is going on with transform-origin properties on the SVG 1.1 transform functions and how to overcome this issue in Chrome 48.


First of all, transform-origin is a pure CSS 3 property, it is not related to SVG 1.1 at all. Despite the fact that transform sounds a lot like transform-origin, they apply to different systems. transform exists in both CSS 3 and SVG 1.1 but have separate implementations. transform-origin only exists in CSS 3 and therefore it is not supposed to influence SVG 1.1. The fact that transform-origin has no influence on SVG in Chrome 48 is expected.

So why transform-origin does apply to SVG in Firefox 44? Well the reason is not exactly clear, but it seems that it is part of the ongoing effort from Mozilla to slowly bring support for SVG 2 in Firefox. Indeed with SVG 2, everything will become a CSS 3 transform (no separate implementation) and SVG will therefore get support for transform-origin. I found out about this in the excellent article about the SVG coordinate systems from Sara Soueidan.


Now how can that be overcome in Chrome 48. It is fairly simple but if you want to apply translate(), scale() and rotate() all the same time, you will still need to calculate the offset induced by the scaling and compensate it in your translation.

As Bobby Orndorff mentioned in his answer, it is actually possible to provide the center of rotation to the rotate() function by providing extra x and y parameters. This is already a great improvement. But unfortunately the scale() function does not support such a thing and will always scale from the top left corner of its parent. Therefore you will still have to correct your translation in order to simulate a scale around a center.

Here is the final solution that works on Chrome 48 and Firefox 44:

<svg width="400" height="400">
  <defs>
    <rect id="shape" width="200" height="200"/>
  </defs>
  <g transform="translate(100,100)">
    <use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
    <ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
    <g transform="translate(50,50) scale(0.5) rotate(45, 100, 100)">
      <use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
    </g>
  </g>
</svg>

Upvotes: 15

Bobby Orndorff
Bobby Orndorff

Reputation: 3335

The example is mixed CSS transform-origin with a SVG transform. While CSS transform and SVG transform are similar, there are differences. For example, CSS transform can be 2D and 3D while SVG transform is only 2D. CSS transform rotate function accepts the angle as a number combined with an unit (e.g. degs, grad, rad, turn) while SVG transforms accepts the angle as a number (with implied unit of degrees) along with optional second and third parameters (x, y) representing the origin of rotation.

To get the example to work in FireFox and Chrome, you could use a CSS transform instead of a SVG transform. For example...

<svg width="400" height="400">
  <defs>
    <rect id="shape" width="200" height="200"/>
  </defs>
  <g transform="translate(100,100)">
    <use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
    <ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
    <g style="transform: translate(0,0) scale(0.5) rotate(45deg); transform-origin: 100px 100px;">
      <use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
    </g>
  </g>
</svg>

To get the example to work in FireFox, Chrome, and IE, you could use the SVG transform rotate function with the optional second and third parameters instead of a CSS transform-origin. For example...

<svg width="400" height="400">
  <defs>
    <rect id="shape" width="200" height="200"/>
  </defs>
  <g transform="translate(100,100)">
    <use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
    <ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
    <g transform="translate(0,0) scale(0.5) rotate(45,200,200)">
      <use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
    </g>
  </g>
</svg>

Upvotes: 3

Related Questions