如何控制“<use>”元素的“transform-b​​ox”?

Zea*_*rin 2 css svg css-transforms shadow-dom svg-transforms

背景

\n

我\xe2\x80\x99m 喜欢 SVG2 中扩展的 CSS 支持。不用一遍又一遍地重写属性,这很好。所以我\xe2\x80\x99一直在将项目中的一些代码从SVG属性转换为CSS。其中大部分效果都很好。

\n

当谈到转换时,如果您熟悉 CSS 转换在 HTML 中的工作方式,那么事情可能会显得很棘手。(这对于rotate()转换尤其如此,这是这个问题的焦点。) \xe2\x80\x99s 因为 SVG 没有 \xe2\x80\x99t 具有 HTML 的 \xe2\x80\x9c 自动流 \xe2\x80\x9d做。

\n

换句话说,当你有一堆 HTML 元素时,它们会一个接一个地根据盒模型自动布局。

\n

SVG 中没有这样的 \xe2\x80\x9cautomatic\xe2\x80\x9d 或 \xe2\x80\x9cdefault\xe2\x80\x9d 布局。因此,SVG 转换默认为从原点计算。(0,0用户坐标中的\xe2\x80\x99s)。

\n
\n

近乎完美的解决方案

\n

对于大多数元素,\xe2\x80\x99 有一个简单的解决方案:很棒的 CSS 属性transform-box。在大多数情况下,使用以下 CSS 将允许您以与 HTML 元素几乎相同的方式转换 SVG 元素:

\n
/* whatever elements you want to transform */\nrect, polygon {      \n    transform-box:    fill-box;  \n    transform-origin: center center;  /* or `top left`, `center right`, etc. */\n}\n\n.rotate90 {\n    transform: rotate(90deg);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

现在,你可以做类似的事情...

\n
<rect class="rotate90" x="123" y="789" width="50" height="50" />\n
Run Code Online (Sandbox Code Playgroud)\n

并且它会围绕transform-originCSS 中指定的位置旋转。由于上面的示例使用了transform-originof center center,因此它会原地旋转。

\n

这与使用 的 HTML 元素的行为相匹配transform: rotate(\xe2\x80\xa6)。并且 \xe2\x80\x94 尤其是如果 \xe2\x80\x99 在 SVG 图像中存在很多像这样的旋转\xe2\x80\x94,它比等效的 SVG 标记要好得多

\n

为什么 CSS 比等效的 SVG 标记更好?

\n

因为SVG\xe2\x80\x99srotate()函数的语法略有不同,因此没有与transform-box: fill-box上面使用的 CSS 等效的语法,除非您为每次旋转指定 X 和 Y 坐标。

\n

这意味着你必须每次都输入旋转点,如下所示:

\n
<rect class="rotate90" x="123" y="789" width="50" height="50" />\n
Run Code Online (Sandbox Code Playgroud)\n
\n

问题

\n

我遇到的问题是CSS 解决方案不适用于<use />元素。

\n

\xe2\x80\x99的原因非常清楚:<use />元素将引用的元素克隆到新位置。因此,就 CSS 而言,它正在转换<use />元素,而不是克隆的 (Shadow-DOM) 内容。

\n

当涉及到将 CSS 应用到<use />\xe2\x80\x94 的其他挑战(例如设置不同的配色方案\xe2\x80\x94)时,有一些解决方案(例如SVG 超级英雄 S​​ara Soueidan 的解决方案)。

\n

但是当涉及到解决坐标问题时,我还没有找到解决办法。

\n

示例代码

\n

编辑:为了更明确地了解 I\xe2\x80\x99m 的用途,这里有一些示例代码。

\n
<rect x="123" y="789" width="50" height="50" \n      transform="rotate(90 123 789)"\n></rect>\n<!-- \n  In this example, the rotation pivots around the X and Y coordinates of the `rect` element.\n  \n  If you wanted to rotate around the center, you would have to calculate:\n  \n  x + width/2\n  y + width/2\n\n  And use those values in the `rotate()` function instead.\n\n -->\n
Run Code Online (Sandbox Code Playgroud)\n
.transform-tl,\n.transform-tc,\n.transform-tr,\n.transform-cl,\n.transform-cc,\n.transform-cr,\n.transform-bl,\n.transform-bc,\n.transform-br {\n    transform-box: fill-box;\n}\n\n.transform-tl { transform-origin: top left; }\n.transform-tc { transform-origin: top center; }\n/*  \n\xe2\x80\xa6and so on, for the other combinations of `transform-origin` keyword values\xe2\x80\xa6 \n*/\n\n.rotate90.cw {\n    transform: rotate(90deg)\n}\n\n.rotate90.ccw {\n    transform: rotate(-90deg)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(感谢 @PaulLeBeau 的推动,包含了这段代码。)

\n
\n

有没有人有办法解决吗?

\n

(即使是解决方案\xe2\x80\x94,只要它比transform在每个<use />\xe2\x80\x94 上指定 SVG 属性更少的重复,也会受到欢迎!)

\n

Pau*_*eau 6

假设与@Sphinxxx假设的条件相同......

\n

然后,您还可以将元素包装<use>在 group ( <g>) 元素中,并对其应用旋转类。

\n

\r\n
\r\n
/* whatever elements you want to transform */\nrect, polygon, use, g {\n    transform-box: fill-box;\n    transform-origin: center center; /* or `top left`, `center right`, etc. */\n}\n\n.rotate45 {\n    transform: rotate(45deg);\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<svg width="300" height="200">\n    <defs>\n      <rect id="rr" x="80" y="60" width="50" height="50" />\n    </defs>\n\n    <use href="#rr" x="100" y="0" fill="tomato" />\n    <g class="rotate45">\n      <use href="#rr" x="100" y="0" fill="lime" />\n    </g>\n</svg>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

这是怎么回事?为什么这有效?

\n

原因与<use>元素如何取消引用以及发生这种情况时发生的转换有关。如果您阅读了SVG 规范中有关元素的部分<use>,它会说:

\n
\n

在生成的内容中,\xe2\x80\x98use\xe2\x80\x99 将替换为 \xe2\x80\x98g\xe2\x80\x99,其中 \xe2\x80\x98use\xe2\x80 中的所有属性\x99 元素,\xe2\x80\x98x\xe2\x80\x99、\xe2\x80\x98y\xe2\x80\x99、\xe2\x80\x98width\xe2\x80\x99、\xe2\x80\x98height 除外\xe2\x80\x99 和 \xe2\x80\x98xlink:href\xe2\x80\x99 被传输到生成的 \xe2\x80\x98g\xe2\x80\x99 元素。附加变换translate(x,y)被附加到生成的\xe2\x80\x98g\xe2\x80\上的\xe2\x80\x98transform\xe2\x80\x99属性的末尾(即右侧) x99,其中 x 和 y 表示 \xe2\x80\x98use\xe2\x80\ 上的 \xe2\x80\x98x\xe2\x80\x99 和 \xe2\x80\x98y\xe2\x80\x99 属性的值x99 元素。

\n
\n

因此,以下 SVG(我认为这将与您尝试的类似):

\n
<use href="#rr" x="100" y="0" fill="lime" class="rotate45" />\n
Run Code Online (Sandbox Code Playgroud)\n

将被解引用为等价的:

\n
<g fill="blue" transform="rotate(45) translate(100,0)">\n  <rect x="80" y="60" width="50" height="50" />\n</g>\n
Run Code Online (Sandbox Code Playgroud)\n

由于您可以将变换视为从右向左应用,因此translate(100,0)将在旋转之前应用变换,这意味着旋转将移动距您想要的位置偏移 100 像素的对象。这就是为什么transform-box: fill-box不适用于<use>元素。

\n

\r\n
\r\n
<use href="#rr" x="100" y="0" fill="lime" class="rotate45" />\n
Run Code Online (Sandbox Code Playgroud)\r\n
<g fill="blue" transform="rotate(45) translate(100,0)">\n  <rect x="80" y="60" width="50" height="50" />\n</g>\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

然而,根据我的解决方案,<g>+<use>设置...

\n
<g class="rotate45">\n  <use href="#rr" x="100" y="0" fill="lime" />\n</g>\n
Run Code Online (Sandbox Code Playgroud)\n

将被解引用为等价的:

\n
<g class="rotate45">\n  <g fill="lime" transform="translate(100,0)">\n    <rect x="80" y="60" width="50" height="50" />\n  </g>\n</g>\n
Run Code Online (Sandbox Code Playgroud)\n

\r\n
\r\n
rect, polygon, use, g  {\n    transform-box: fill-box;\n    transform-origin: center center; /* or `top left`, `center right`, etc. */\n}\n\n.rotate45 {\n    transform: rotate(45deg);\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<svg width="300" height="200">\n    <defs>\n      <rect id="rr" x="80" y="60" width="50" height="50" />\n    </defs>\n\n    <use href="#rr" x="100" y="0" fill="tomato" />\n\n    <use href="#rr" x="100" y="0" fill="blue" stroke="blue" class="rotate45" />\n\n    <g fill="lime" transform="rotate(45) translate(100,0)">\n      <rect x="80" y="60" width="50" height="50" />\n    </g>\n</svg>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

在此版本中,两个变换操作分别应用于不同的元素,因此 和transform-box分别transform-origin应用。并且变换原点对翻译没有影响。

\n

所以这意味着transform-box旋转的计算(即在 上<g>)将应用于已经翻译的对象。所以它会按照你想要的方式运行。

\n

在之前的日子里transform-box,这两个例子会给出相同的结果。

\n