I found inconsistencies in Firefox compared to Chrome, Edge, and Opera. Each browser handles transform-origin fine when using a CSS class. However, when I place transform-origin on an SVG element as an attribute, FF ignores the effect. Demo code below. My main question is how to get around this, but I'm also curious to know if this is expected behavior.
CSS transform-origin works in FF.
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 1000'>
<style>
.centered{
transform-origin: center;
}
</style>
<path fill='#500' d='M500 500 400 400 400 600 600 600 600 400z' transform='scale(2)' class='centered'/>
</svg>
Inline SVG doesn't seem to recognize transform-origin (works consistently in Chrome/edge)
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1000 1000'>
<path fill='#500' d='M500 500 400 400 400 600 600 600 600 400z' transform='scale(2)' transform-origin='center'/>
</svg>
Edit: Another user pointed out this question is similar to How to set transform origin in SVG, but their premise is super broad (how to) and either wrong or outdated (posted 8.5 years ago).
"I tried using the transform-origin attribute, but it does not affect anything."
Perhaps browser support has improved, but my question specifically shows transform-origin works perfect as a CSS rule in all modern browsers or as a presentational attribute in all modern browsers except FireFox. That differentiation is likely what led to one solution being unique to this thread and a second solution that is a new take on the other threads answers.
Not to mention their question is centered around JavaScript implementation and was published at a time when FireFox didn't recognize keywords like "center" or unitless numbers which detracts from the core SVG markup problem which is that FireFox interprets the presentational attribute transform-origin='' different than other browsers.
I've solved the problem but I am (as yet) unable to give you a comprehensive reasoning why it works.
The first important thing to know is that you can chain SVG Transforms.
So, wherever you write
transform="scale(2)", you can add atranslate(x, y)to the chain, like this:So far, so good... but if
scaleis2, then what values should we give toxandyoftranslate?To find out, I decided to superimpose larger and subimpose smaller scaled versions of your SVG shape (one for each colour of the rainbow) and see what patterns I could find.
Over the top of your grey shape, I positioned an identically sized green shape.
I gave the green shape a transform of:
so that it would be exactly congruent with your original grey shape.
Then I set about subimposing larger scaled versions (yellow, orange, red) and superimposing smaller scaled versions (blue, indigo, violet).
I predicted that
xandyin each case would relate to thescalefactor applied to that shape and also to the overall size of the originalviewBox.With 3 smaller versions and 3 larger versions, the pattern emerged:
8times as large /x&ytransform value is50% of ((1000 / 8) - 1000)4times as large /x&ytransform value is50% of ((1000 / 4) - 1000)2times as large /x&ytransform value is50% of ((1000 / 2) - 1000)1times as large /x&ytransform value is50% of ((1000 / 1) - 1000)0.5times as large /x&ytransform value is50% of ((1000 / 0.5) - 1000)0.25times as large /x&ytransform value is50% of ((1000 / 0.25) - 1000)0.125times as large /x&ytransform value is50% of ((1000 / 0.125) - 1000)From this, we can conclude, that if you are positioning a shape centered at
50%, 50%of yourviewBoxand you want to display the shape in that same position withscale(2), you must also apply atranslateforxof:where
50%corresponds to thexposition you want to centre the shape over.And a
translateforyof:where
50%corresponds to theyposition you want to centre the shape over.This works consistently, but I haven't spent enough time staring at it yet, to properly understand why.
Working Example: