为什么 transform-origin 不适用于使用内联 SVG 的 Firefox,还有其他选择吗?

Why does transform-origin not work for Firefox using inline SVG and is there an alternative?

我发现 Firefox 与 Chrome、Edge 和 Opera 相比存在不一致之处。当使用 CSS class 时,每个浏览器都能很好地处理 transform-origin。但是,当我将 transform-origin 作为属性放在 SVG 元素上时,FF 会忽略该效果。下面的演示代码。我的主要问题是如何解决这个问题,但我也很想知道这是否是预期的行为。

CSS transform-origin 在 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>

内联 SVG 似乎无法识别 transform-origin(在 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>


编辑:另一位用户指出这个问题类似于 How to set transform origin in SVG,但他们的前提非常宽泛(如何做)并且错误或过时(8.5 年前发布)。

"I tried using the transform-origin attribute, but it does not affect anything."

也许浏览器支持有所改进,但我的问题具体表明 transform-origin 在所有现代浏览器中作为 CSS 规则完美运行,或者在除 FireFox 之外的所有现代浏览器中作为表示属性完美运行。这种差异可能导致一个解决方案是该线程独有的,而第二个解决方案是对其他线程答案的新解决方案。

更不用说他们的问题围绕 JavaScript 实现,并且是在 FireFox 无法识别关键字 "center" 或无单位数字的时候发布的,这有损于核心 SVG 标记问题是 FireFox 解释表示属性 transform-origin='' 与其他浏览器不同。

我已经解决了这个问题,但我(到目前为止)无法给你一个全面的推理为什么它有效。

首先要知道的是,您可以 chain SVG Transforms.

所以,无论你在哪里写transform="scale(2)",你都可以在链中添加一个translate(x, y),像这样:

transform="scale(2) translate(x, y)"

到目前为止,很好...但是如果 scale2,那么我们应该给 [=21= 的 xy 什么值]?

为了找出答案,我决定叠加更大和更小比例的 SVG 形状版本(彩虹的每种颜色一个),看看我能找到什么图案。

在你的 grey 形状的顶部,我放置了一个相同大小的 green 形状。

我给 green 形状一个变换:

transform="scale(1) translate(0, 0)"

以便它与您原来的 灰色 形状完全一致。

然后我着手对更大比例的版本进行子拼版(黄色橙色红色)并叠加较小比例的版本(blueindigoviolet)。

我预测 xy 在每种情况下都会与应用于该形状的 scale 因素以及原始 viewBox 的整体大小有关.

有了 3 个较小的版本和 3 个较大的版本,模式出现了:

  • 红色, 8 倍大 / x & y 转换值是 50% of ((1000 / 8) - 1000)
  • 橙色, 4倍大/x & y转换值是50% of ((1000 / 4) - 1000)
  • 黄色,2倍大/x&y变换值是50% of ((1000 / 2) - 1000)
  • 绿色1倍大/x & y转换值是50% of ((1000 / 1) - 1000)
  • Blue, 0.5 倍大/x & y 转换值是 50% of ((1000 / 0.5) - 1000)
  • Indigo, 0.25 倍大 / x & y 转换值是 50% of ((1000 / 0.25) - 1000)
  • Violet, 0.125 倍大 / x & y 转换值是 50% of ((1000 / 0.125) - 1000)

据此,我们可以得出结论,如果您将一个形状定位在 viewBox50%, 50% 中心,并且您希望在 scale(2) 的相同位置显示该形状,您必须申请translate for x of:

50% of ((width of canvas / scale-factor) - (width of canvas))

其中 50% 对应于您希望形状居中的 x 位置。

translate y

50% of ((height of canvas / scale-factor) - (height of canvas))

其中 50% 对应于您希望形状居中的 y 位置。

它始终如一地工作,但我还没有花足够的时间盯着它看,以正确理解原因。

工作示例:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">

<!-- Grey Original -->
<path fill="#555" d="M500 500 400 400 400 600 600 600 600 400z" />

<!-- Red Transform [50% of ((1000 / 8) - 1000) is -437.5] -->
<path fill="rgb(255, 0, 0)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(8) translate(-437.5, -437.5)" />

<!-- Orange Transform [50% of ((1000 / 4) - 1000) is -375] -->
<path fill="rgb(255, 125, 0)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(4) translate(-375, -375)" />

<!-- Yellow Transform [50% of ((1000 / 2) - 1000) is -250] -->
<path fill="rgb(255, 255, 0)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(2) translate(-250, -250)" />

<!-- Green Transform [50% of ((1000 / 1) - 1000) is 0] -->
<path fill="rgb(0, 125, 0)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(1) translate(0, 0)" />

<!-- Blue Transform [50% of ((1000 / 0.5) - 1000) is 500] -->
<path fill="rgb(0, 0, 125)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(0.5) translate(500, 500)" />

<!-- Indigo Transform [50% of ((1000 / 0.25) - 1000) is 1500] -->
<path fill="rgb(63, 0, 255)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(0.25) translate(1500, 1500)" />

<!-- Violet Transform [50% of ((1000 / 0.125) - 1000) is 3500] -->
<path fill="rgb(199, 125, 243)" d="M500 500 400 400 400 600 600 600 600 400z" transform="scale(0.125) translate(3500, 3500)" />

</svg>

总的来说,标记为正确的答案在所有情况下都有效。但是,我正在编写另一个解决方案,因为它最容易实现。但请注意,它仅适用于 transform 而不适用于 patternTransform。虽然非 FireFox 浏览器可以识别 transform-origin 这两个属性,但我的解决方案不适用于 FireFox 上的 patternTransform

在样式属性 (style='transform-origin:center') 中添加 transform-origin 而不是其自己的表示属性 (transform-origin='center') 适用于 transform,如下所示。这甚至适用于数据 URI 中的 SVG。

<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)' style='transform-origin:center'/>
</svg>