likes
comments
collection
share

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

作者站长头像
站长
· 阅读数 17

有些无心插柳柳成荫的偶然,就像夏天某个夜晚乍起的清风,让你尚未意识到闷热,便沐浴到一阵清凉,猝不及防又豁然开朗,我不知道该如何表达这份不期而遇+,只能将其归结为巧了。

本想解决SVG3D转换中的视点问题,却无意间看到了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-boxview-boxborder-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>

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

此时,我们想让<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奇淫巧技(九):SVG Transform的破壁人——transform-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)"/>

结果如下:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

OK,方案一验证成功。

方案二:使用CSS transform

这个方案的思路很清晰,既然SVG transform的路上,万恶的左上角无法“逾越”,那我们干脆换一条赛道不就好了。

我们使用CSStransform-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);
}

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

CSS的降维打击下,SVG的左上角也只能言听计从。但是,无论是方案一还是方案二,让矩形实现围绕自身的中点旋转都很繁琐,要先计算矩形的中点位置,然后定位到这里之后再进行旋转,索性案例中的中心点比较好算,如果是一个很复杂的多边形的话,除非欧几里得附体,否则凭借我硕果仅存的几何知识就只剩下望洋兴叹的份了。

那除了上述两种方案之外,还有什么方式能让矩形优雅的实现坦克掉头呢?接下来开始今天的正文内容。

左上角的破壁人fill-box

还是刚才的案例,矩形围绕自身蓝色圆心旋转,由于这个属性过于优雅,索性我们直接加上旋转的动画来聊表敬意。

SVG奇淫巧技(九):SVG Transform的破壁人——transform-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>xy,如此一来,我们只需要指定transform-origin: 50% 50%即为 <rect> 的中心点,就算是一个很复杂的多边形,也不需要我们自己再去计算坐标啦。

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

看到这里我已经词穷了,想说的除了优雅还是优雅。

transform-box的其他属性

写到这里,今天的主角 fill-box 就已经介绍完了,其实根据MDN的英文文档介绍,transform-box一共有5个可选值,分别为:content-boxborder-boxfill-boxstroke-boxview-box

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

其中,fill-boxview-box 我们已经介绍过了,至于其他3个属性嘛,大致意思如下:

  1. content-box使用元素的内容框作为参考框盒子;
  2. border-box使用元素的边框框作为参考框盒子;
  3. stroke-box使用对象的描边边界框作为参考框盒子。

虽然文档云里雾里的不说人话,但身为程序员我们可以让代码来充当翻译,用案例来对文字注释啊,却没成想,ChromeEdge 直接油盐不进:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

Firefox稍好,但也不多:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

虽然,火狐多支持了一个border-box,但在实验的案例中,如果按照文档描述来看,transform-box: border-box将变换限制为仅应用于边框盒子内的内容,所以在元素在进行变换时,不会改变其边框的形状,但真实的结果是这个属性并未起到任何作用:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

即便transform-box的兼容性并不算低,但主流的浏览器似乎依旧对这个属性弃之敝履:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

对于这另外三个属性,大家作为扩展阅读了解一下即可,不必较真,不过,在CSS规范中有这样一段描述:

SVG奇淫巧技(九):SVG Transform的破壁人——transform-box

这个规范中提到了几个特殊的元素:

  1. <pattern> 元素的参考框由 patternUnits属性定义;
  2. linearGradientradialGradient 元素的参考框由 gradientUnits 属性定义;
  3. clipPath元素的参考框由 clipPathUnits 属性定义。

参考框为 transform-origin 属性指定的原点添加了额外的偏移量。

这几个特殊的元素之所以参考框“身不由己”,也是有很多小秘密在这些决定性属性中的,有机会我们单开一篇再谈,以上这些大家作为了解就好啦。

transform-box小结

transform-box 是一个 CSS 属性,用于指定 CSS 变换的基准盒模型,即变换的参考系,需要配合transform-origin来使用,其中view-box为默认值,而fill-box可以将身为SVG的元素突破视窗左上角的魔咒,从此那些打破桎梏的元素,也享受到了CSS中元素的权利 —— 选择的权利,我仿佛看到了SVG王朝霸权的土崩瓦解,不由得一阵唏嘘,真是其兴也勃焉,其亡也忽焉。

都看到这里了,请大家不要吝啬一个小小的点赞,谢谢!