css - 如何为`控制`transform-box`
问题描述
背景
我喜欢 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 中指定的旋转。由于上面的示例使用 a transform-origin
of center center
,因此它在原地旋转。
这与使用transform: rotate(…)
. 而且——特别是如果 SVG 图像中有很多这样的旋转——它比等效的 SVG 标记要好得多。
为什么 CSS 比等效的 SVG 标记更好?
因为SVG 的rotate()
函数语法略有不同,没有与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 />
元素将引用的元素克隆到新位置。因此,就 CSS 而言,它正在转换<use />
元素,而不是克隆的 (Shadow-DOM) 内容。
当涉及到将 CSS 应用于其他挑战时<use />
——例如设置不同的配色方案——有一些解决方案(比如来自 SVG 超级英雄 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>
元素包装在一个 group ( <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>
取消引用元素的方式以及发生这种情况时转换会发生什么有关。如果您阅读SVG 规范中关于元素的部分<use>
,它会说:
在生成的内容中,“use”将被“g”替换,其中“use”元素中除“x”、“y”、“width”、“height”和“xlink:href”之外的所有属性都是转移到生成的'g'元素。附加转换 translate(x,y) 附加到生成的 'g' 上的 'transform' 属性的末尾(即右侧),其中 x 和 y 表示 'x' 和 'y' 的值'use' 元素的属性。
所以,下面的 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
,这两个例子会给出相同的结果。
推荐阅读
- swift - 如何在 xcode swift 上将对象存储在本地数据库中
- visual-studio-code - 在 VS Code 中,项目与工作区或文件夹有什么区别?
- mysql - 执行 INNER JOIN 后相乘和求和
- html - 如何制作一个顶行和右列合并的 2x2 表?
- angular - Angular 8 setErrors 将控件设置为无效但不在模板上更新
- ansible-awx - 获取 SCM 分支/标签名称作为剧本变量
- netbeans - 切换项目组时当前文档组被清除/重置
- amazon-web-services - AWS Lambda 函数如何响应多记录事件负载,即 Lambda 代码是每批运行一次还是多次运行?
- r - data.table:检索两个数据表之间匹配键的逻辑向量
- java - 通用铸造问题