背景
我喜欢 SVG2 中扩展的 CSS 支持。不必一遍又一遍地重写属性真是太好了。所以我一直在将元素中的一些代码从 SVG 属性转换为 CSS。其中大部分工作正常。
当涉及到转换时,如果您熟悉 CSS 转换在 HTML 中的工作方式,事情可能看起来很棘手。 (对于 rotate()
转换尤其如此,这是本问题的重点。)这是因为 SVG 没有 HTML 所具有的“自动流”。
换句话说,当你有一堆 HTML 元素时,一个接一个,它们会自动按照盒子模型自行布局。
SVG 中没有这样的“自动”或“默认”布局。因此,SVG 转换默认为从原点计算。 (即用户坐标中的 0,0
)。
几乎-完美的解决方案
对于大多数元素,有一个简单的解决方案:很棒的 CSS 属性 transform-box
.在大多数情况下,使用以下 CSS 将允许您以与 HTML 元素几乎相同的方式转换 SVG 元素:
/* whatever elements you want to transform */
rect, polygon {
transform-box: fill-box;
transform-origin: center center; /* or `top left`, `center right`, etc. */
}
.rotate90 {
transform: rotate(90deg);
}
现在,您可以做类似...
<rect class="rotate90" x="123" y="789" width="50" height="50" />
它会围绕transform-origin
旋转在 CSS 中指定。由于上面的示例使用了 transform-origin
的 center center
, 它原地旋转。
匹配使用 transform: rotate(…)
的 HTML 元素的行为.而且——特别是如果 SVG 图像中有很多像这样的旋转——它比等效的 SVG 标记方式要好得多。
为什么 CSS 优于等效的 SVG 标记?
因为 SVG’s rotate()
function has a slightly different syntax没有等同于 transform-box: fill-box
的上面使用的 CSS,除非您为每个旋转指定 X 和 Y 坐标。
这意味着您必须每次都输入旋转点,如下所示:
<rect x="123" y="789" width="50" height="50"
transform="rotate(90 123 789)"
></rect>
<!--
In this example, the rotation pivots around the X and Y coordinates of the `rect` element.
If you wanted to rotate around the center, you would have to calculate:
x + width/2
y + width/2
And use those values in the `rotate()` function instead.
-->
问题
我遇到的问题是CSS 解决方案不适用于<use />
元素。
很明显为什么:<use />
element 将引用的元素克隆到新位置。因此,就 CSS 而言,它正在转换 <use />
。元素,而不是克隆的 (Shadow-DOM) 内容。
涉及将 CSS 应用于 <use />
的其他挑战时—例如设置不同的配色方案—有解决方案(如 this one from SVG superhero Sara Soueidan )。
但是当涉及到解决坐标的问题时,我还没有想出解决办法。
示例代码
编辑:为了更明确地说明我要做什么,这里有一些示例代码。
.transform-tl,
.transform-tc,
.transform-tr,
.transform-cl,
.transform-cc,
.transform-cr,
.transform-bl,
.transform-bc,
.transform-br {
transform-box: fill-box;
}
.transform-tl { transform-origin: top left; }
.transform-tc { transform-origin: top center; }
/*
…and so on, for the other combinations of `transform-origin` keyword values…
*/
.rotate90.cw {
transform: rotate(90deg)
}
.rotate90.ccw {
transform: rotate(-90deg)
}
<!--
Using the CSS classes in the following manner works as intended for most SVG elements as intended.
But with the `<use />` element, the rotation does not pivot around the top left corner, as expected... :(
-->
<use
class="transform-tl rotate90 cw"
x ="0"
y ="1052"
href ="#block-A12-2"
></use>
(感谢@PaulLeBeau 插入包含这段代码。)
有人有解决方案吗?
(即使是变通解决方案——只要它涉及的重复次数少于在每个 transform
上指定 SVG <use />
属性——也是受欢迎的!)
最佳答案
假设与@Sphinxxx 假设的条件相同......
然后你也可以只包装你的 <use>
组 ( <g>
) 元素中的元素并将旋转类应用于该元素。
/* whatever elements you want to transform */
rect, polygon, use, g {
transform-box: fill-box;
transform-origin: center center; /* or `top left`, `center right`, etc. */
}
.rotate45 {
transform: rotate(45deg);
}
<svg width="300" height="200">
<defs>
<rect id="rr" x="80" y="60" width="50" height="50" />
</defs>
<use href="#rr" x="100" y="0" fill="tomato" />
<g class="rotate45">
<use href="#rr" x="100" y="0" fill="lime" />
</g>
</svg>
这是怎么回事?为什么会这样?
原因与<use>
的方式有关元素被取消引用,发生这种情况时会发生什么变化。如果你阅读 the section about <use>
elements in the SVG spec ,它说:
In the generated content, the ‘use’ will be replaced by ‘g’, where all attributes from the ‘use’ element except for ‘x’, ‘y’, ‘width’, ‘height’ and ‘xlink:href’ are transferred to the generated ‘g’ element. An additional transformation translate(x,y) is appended to the end (i.e., right-side) of the ‘transform’ attribute on the generated ‘g’, where x and y represent the values of the ‘x’ and ‘y’ attributes on the ‘use’ element.
所以,下面的 SVG(我想这会像你正在尝试的那样):
<use href="#rr" x="100" y="0" fill="lime" class="rotate45" />
将被取消引用为等同于:
<g fill="blue" transform="rotate(45) translate(100,0)">
<rect x="80" y="60" width="50" height="50" />
</g>
由于您可以认为变换是从右向左应用的,因此 translate(100,0)
将在旋转之前应用,这意味着旋转将移动偏离您想要的位置 100 像素的物体。这就是为什么 transform-box: fill-box
不适用于 <use>
元素。
rect, polygon, use, g {
transform-box: fill-box;
transform-origin: center center; /* or `top left`, `center right`, etc. */
}
.rotate45 {
transform: rotate(45deg);
}
<svg width="300" height="200">
<defs>
<rect id="rr" x="80" y="60" width="50" height="50" />
</defs>
<use href="#rr" x="100" y="0" fill="tomato" />
<use href="#rr" x="100" y="0" fill="blue" stroke="blue" class="rotate45" />
<g fill="lime" transform="rotate(45) translate(100,0)">
<rect x="80" y="60" width="50" height="50" />
</g>
</svg>
然而,对于我的解决方案,<g>
+ <use>
设置...
<g class="rotate45">
<use href="#rr" x="100" y="0" fill="lime" />
</g>
将被取消引用为等同于:
<g class="rotate45">
<g fill="lime" transform="translate(100,0)">
<rect x="80" y="60" width="50" height="50" />
</g>
</g>
rect, polygon, use, g {
transform-box: fill-box;
transform-origin: center center; /* or `top left`, `center right`, etc. */
}
.rotate45 {
transform: rotate(45deg);
}
<svg width="300" height="200">
<defs>
<rect id="rr" x="80" y="60" width="50" height="50" />
</defs>
<use href="#rr" x="100" y="0" fill="tomato" />
<g class="rotate45">
<use href="#rr" x="100" y="0" fill="blue" stroke="blue"/>
</g>
<g class="rotate45">
<g fill="lime" transform="translate(100,0)">
<rect x="80" y="60" width="50" height="50" />
</g>
</g>
</svg>
在此版本中,两个变换操作中的每一个都应用于不同的元素,因此 transform-box
和 transform-origin
分别申请。并且变换原点对平移没有影响。
那么这意味着 transform-box
旋转计算(即在 <g>
上)将应用于已翻译的对象。所以它会按照您的意愿行事。
前几天 transform-box
, 这两个例子会给出相同的结果。
关于css - `transform-box`元素如何控制 `<use>`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67651011/