SVG奇淫巧技(九):SVG Transform的破壁人——transform-box
有些无心插柳柳成荫的偶然,就像夏天某个夜晚乍起的清风,让你尚未意识到闷热,便沐浴到一阵清凉,猝不及防又豁然开朗,我不知道该如何表达这份不期而遇+,只能将其归结为巧了。
本想解决SVG
在3D
转换中的视点问题,却无意间看到了transform-box
这个未知属性,本着试试看的心态去查询了一下,在查阅了少得可怜的资料和谜语人一样的文档描述之后,我突然意识到,这个属性简直就是《SVG奇淫巧技(七):SVG Transform之万恶的左上角》的破壁人,老规矩先来扒一下transform-box
定义。
transform-box的定义
MDN
transform-box
: The transform-box CSS property defines the layout box to which the transform, individual transform properties translate,scale, and rotate, and transform-origin properties relate.
简单概括一下,transform-box
属性可以定义 transform
的各种变换与transform-origin
这两个属性之间以何种形式进行关联。
transform-box
属性有3个可选值:fill-box
、view-box
和border-box
。默认值是 view-box
。
view-box
:在应用变换时,以元素的视口(视区)为参考框模型。
fill-box
:在应用变换时,以元素的填充盒子(包括内边距和内容区域)为参考框模型
border-box
:在应用变换时,以元素的边框盒子(包括边框、内边距和内容区域)为参考框模型。
说实话,这不是翻译的不够信达雅,是原文档就是如此的不说人话。不过,读不懂没关系,实践出真知,代码诚不欺我。
view-box
首先,我们来画一个粉色边框的SVG
画布,然后在中心画一个红色的圆点,再在左上角画一个黑色的正方形,并在这个正方形的中心画一个蓝色的圆点,代码如下:
<svg width="800px" height="800px" viewBox="0 0 50 50"
style="border: 10px solid pink">
<g>
<circle id="container-center" cx="25" cy="25" r="1" fill="red" />
<circle id="box-center" cx="15" cy="15" fill="blue" r=".5" />
<rect id="box" x="10" y="10" width="10" height="10" rx="1" ry="1"
stroke="black" fill="none" />
</g>
</svg>
此时,我们想让<rect>
旋转45°
,代码改造如下:
<rect id="box" x="10" y="10" width="10" height="10" rx="1" ry="1"
stroke="black" fill="none" transform="rotate(45)"/>
因为,tarnsform-box
的默认值即为view-box
,所以等同于:
#box {
transform-box: view-box;
}
结果如下:
不知道是否与大家颅内渲染的结果大相径庭,出现这种结果的原因就是SVG Transform
是针对视图的左上角(0, 0)
为基准进行变换的,也就是说上述例子中的<rect>
旋转的45°
是围绕着(0, 0)
的坐标进行的变换,并不是围绕自身的蓝点。
那么请大家思考5s
,有几种方法可以让<rect>
围绕自身的蓝色的圆点旋转45°
呢?
7...5...3...2...1...
一个方法都没想到的同学,罚看《SVG奇淫巧技(七):SVG Transform之万恶的左上角》一遍。
方案一:指定SVG transform
的旋转中心
SVG Transform
中关于rotate
的语法是:rotate(angle[x, y])
,其中[x, y]
为可选参数,作用就是用来设置便宜变换中心的。
是不是看到这里就豁然开朗了,我们直接为<rect>
指定蓝色圆点为旋转中心不就万事大吉了。
代码改造如下:
<rect id="box" x="10" y="10" width="10" height="10" rx="1" ry="1"
stroke="black" fill="none" transform="rotate(45, 15 15)"/>
结果如下:
OK,方案一验证成功。
方案二:使用CSS transform
这个方案的思路很清晰,既然SVG transform
的路上,万恶的左上角无法“逾越”,那我们干脆换一条赛道不就好了。
我们使用CSS
的transform-origin
来定位到矩形的圆心蓝点,然后再使用rotate
旋转45°
。
代码改造如下:
<rect id="box" x="10" y="10" width="10" height="10" rx="1" ry="1"
stroke="black" fill="none"/>
#box {
transform-box: view-box;
transform-origin: 15px 15px;
transform: rotate(45deg);
}
在CSS
的降维打击下,SVG
的左上角也只能言听计从。但是,无论是方案一
还是方案二
,让矩形实现围绕自身的中点旋转都很繁琐,要先计算矩形的中点位置,然后定位到这里之后再进行旋转,索性案例中的中心点比较好算,如果是一个很复杂的多边形的话,除非欧几里得附体,否则凭借我硕果仅存的几何知识就只剩下望洋兴叹的份了。
那除了上述两种方案之外,还有什么方式能让矩形优雅的实现坦克掉头呢?接下来开始今天的正文内容。
左上角的破壁人fill-box
还是刚才的案例,矩形围绕自身蓝色圆心旋转,由于这个属性过于优雅,索性我们直接加上旋转的动画来聊表敬意。
代码改动如下:
<rect id="box" x="10" y="10" width="10" height="10" rx="1" ry="1"
stroke="black" fill="none"/>
#box {
transform-origin: 50% 50%;
transform-box: fill-box;
animation: rotation 3s linear infinite;
}
@keyframes rotation {
to {
transform: rotate(360deg);
}
}
从代码上就不难看出,fill-box
属性在应用变换时,以元素的填充盒子(包括内边距和内容区域)为参考框模型从而进行变换,翻译成人话就是,为矩形<rect>
添加了 transform-box: fill-box
属性之后,变换的基点从SVG
的视图窗口的(0, 0)
转变为了矩形自身的左上角也就是 <rect>
的 x
和 y
,如此一来,我们只需要指定transform-origin: 50% 50%
即为 <rect>
的中心点,就算是一个很复杂的多边形,也不需要我们自己再去计算坐标啦。
看到这里我已经词穷了,想说的除了优雅还是优雅。
transform-box的其他属性
写到这里,今天的主角 fill-box
就已经介绍完了,其实根据MDN
的英文文档介绍,transform-box
一共有5
个可选值,分别为:content-box
、border-box
、fill-box
、stroke-box
和 view-box
。
其中,fill-box
和 view-box
我们已经介绍过了,至于其他3
个属性嘛,大致意思如下:
content-box
使用元素的内容框作为参考框盒子;border-box
使用元素的边框框作为参考框盒子;stroke-box
使用对象的描边边界框作为参考框盒子。
虽然文档云里雾里的不说人话,但身为程序员我们可以让代码来充当翻译,用案例来对文字注释啊,却没成想,Chrome
和 Edge
直接油盐不进:
Firefox
稍好,但也不多:
虽然,火狐多支持了一个border-box
,但在实验的案例中,如果按照文档描述来看,transform-box: border-box
将变换限制为仅应用于边框盒子内的内容,所以在元素在进行变换时,不会改变其边框的形状,但真实的结果是这个属性并未起到任何作用:
即便transform-box
的兼容性并不算低,但主流的浏览器似乎依旧对这个属性弃之敝履:
对于这另外三个属性,大家作为扩展阅读了解一下即可,不必较真,不过,在CSS
规范中有这样一段描述:
这个规范中提到了几个特殊的元素:
<pattern>
元素的参考框由patternUnits
属性定义;linearGradient
、radialGradient
元素的参考框由gradientUnits
属性定义;clipPath
元素的参考框由clipPathUnits
属性定义。
参考框为 transform-origin 属性指定的原点添加了额外的偏移量。
这几个特殊的元素之所以参考框“身不由己”,也是有很多小秘密在这些决定性属性中的,有机会我们单开一篇再谈,以上这些大家作为了解就好啦。
transform-box
小结
transform-box
是一个 CSS
属性,用于指定 CSS
变换的基准盒模型,即变换的参考系,需要配合transform-origin
来使用,其中view-box
为默认值,而fill-box
可以将身为SVG
的元素突破视窗左上角的魔咒,从此那些打破桎梏的元素,也享受到了CSS
中元素的权利 —— 选择的权利,我仿佛看到了SVG
王朝霸权的土崩瓦解,不由得一阵唏嘘,真是其兴也勃焉,其亡也忽焉。
都看到这里了,请大家不要吝啬一个小小的点赞,谢谢!
转载自:https://juejin.cn/post/7206226905308659770