border-radius的本质
前言
border-radius
从名字上看像是指定border-box
的圆角半径,实际上在使用时大家也是这么用的;但是从W3C官方文档定义来看,border-radius
属性不仅会影响border-box
的形状,还能影响padding-box
和content-box
的形状;
border-radius
的本质就是通过定义盒模型四个顶点的圆角半径,从而确定四个顶点圆角的中心位置,四个中心点绘制的椭圆/圆分别对border-box
,padding-box
和content-box
进行裁剪;
所以border-radius
本质上是一种特殊的形状裁剪语法,和clip-path
类似;
语法
border-radius
本身是一种缩写属性,对应四个顶点的设置:
border-top-left-radius
border-top-right-radius
border-bottom-right-radius
border-bottom-left-radius
这种缩写方式与border
属性类似,顶点顺序也是遵循顺时针方向,从左上到左下;
值的类型
<length>
:绝对长度类型;<percentage>
:百分比类型,百分比基于半轴对应方向border-box
的尺寸;
维度/方向
如果是在单个顶点设置圆角属性时,是以空格来分隔两个维度的半轴长度,前者为x
方向半轴长度,后者为y
方向半轴长度:
1 | .demo { |
而在缩写语法中,通过斜杠(/
)来分隔不同方向的半轴长度,前面为x
方向半轴长度的缩写,后面为y
方向半轴长度的缩写;
缩写语法
border-radius
缩写属性可接收1~4个值,不同数量的值会有不同的顶点分配:
-
1个值:每个顶点具有一样的设置;
1
2
3
4
5
6
7
8
9.demo {
border-radius: 10px;
}
/* or */
.demo {
border-radius: 10px / 5%;
} -
2个值:第一个值对应左上和右下顶点,第二个值对应右上和左下顶点;可以看出这种语法是对角设置;
1
2
3.demo {
border-radius: 10px 20px;
} -
3个值:第一个值对应左上顶点,第二个值对应右上和左下顶点(对角线),第三个值对应右下顶点;
1
2
3.demo {
border-radius: 10px 5% 20px;
} -
4个值:四个值依次对应左上,右上,右下,左下四个顶点;
1
2
3.demo {
border-radius: 10px 30px 5% 11px;
}
裁剪原理
以上关于语法的部分只不过是随处可见的资料,然而border-radius
本质上是对盒模型进行形状裁剪,因此了解其裁剪原理才算是理解其本质;
注:圆是一种特殊的椭圆,下面用椭圆来替代圆/椭圆,不再赘述;
- 根据顶点圆角两个维度的半轴长度确定椭圆的中心点;
- 从中心点画出椭圆弧,对相应的盒模型进行裁剪,裁剪后的形状就是新的盒模型形状(即可能不是正常的盒形);
从原理上来说是很简单的,但是其实还存在很多细节,一些边界情况如何处理等等;
确定中心点
border-box
的中心点其实很好确定,毕竟border-radius
指定的就是border-box
顶点圆角的半轴长,根据border-box
顶点位置和半轴长马上就能得到中心点位置;
其实其他盒模型的中心点的位置跟border-box
的位置是一致的,关键在于半轴长度如何确定?毕竟border-radius
只是指定了border-box
圆角的半轴长度,因此其他盒模型的半轴长度只能基于此进行推导;
其他盒模型的半轴长
The padding edge (inner border) radius is the outer border radius minus the corresponding border thickness. In the case where this results in a negative value, the inner radius is zero.[1]
根据W3C
文档的定义可知:
注:为border-box
的半轴长,为水平方向的边框长度(顶点在左边就是左边框,在右边就是右边框),同理,就是竖直方向的边框长度;
当然,如果得到的半轴长为负值时,会自动变成0
,此时也就没有圆角裁剪效果了;同理可以得到content-box
的圆角半轴长:
裁剪规则
上面为了图示方便画的都是一个完整的椭圆,实际上只需要四分之一个椭圆即可;具体是哪四分之一,需要根据顶点的方向来确定,即对应方向的象限(这里借用坐标象限表述可能清楚一些);
- 左上:第二象限
- 右上:第一象限
- 右下:第四象限
- 左下:第三象限
裁剪时椭圆曲线外被舍去,也可以理解为对应的椭圆曲线充当新的盒模型外曲线;以border-box
为例:
其它顶点及盒模型裁剪也是类似的;
半轴长度的上限
从规范上来看,好像并没有提及半轴长度的上限,也就是只要半轴长度大于等于0
理论上都是可行的;可是经过实践(blink
内核和webkit
内核),浏览器上面的表现可以看出半轴长度是存在一个上限的,这个上限就是盒模型相应方向尺寸的一半!
当然,这并不是说当半轴长度超过盒模型相应方向尺寸的一半时,简单地按尺寸的一半进行处理;而是根据当前盒模型的宽高比进行一定的缩放,即把超出比例最多的方向的半轴长度设置为当前盒模型对应方向尺寸的一半,另一个方向按比例缩放!
举个例子:一个元素的border-box
是一个300x200
的box
时,设置border-radius
为150px/200px
,由于此时y
方向半轴长度是超出比例最多的,因此会将y
方向的半轴长度设置为box
高度的一半——100px
,那么此时另一个方向——x
方向的半轴长度就是:
因此,此时border-radius: 150px/200px
就等价于border-radius: 75px/100px
;
裁剪后的padding与border
从上面可以很明显的看出,经过border-radius的裁剪,border和padding也变形了;其实可以这么理解:
border-box
外曲线与padding-box
外曲线之间的区域就是border
;- 同理,
padding-box
外曲线与content-box
外曲线之间的区域就是padding
;
案例分析
绘制蛋形
从鸡蛋形状可以分析:上半部形状较尖,下半部形状较圆;根据border-radius原理出发,想要绘制鸡蛋形状,上面两个顶点的椭圆肯定偏窄,下面两个顶点的的椭圆就圆一点;所谓偏窄就是y
方向的半轴长度更大,得出css
如下:
1 | .egg { |
胶囊分离动画
上面这个动画仅需一个元素,无需借助伪元素,如果理解了border-radius
的本质,就很容易想明白的,可以挑战一下;
相关文档
- border-radius - CSS(层叠样式表) | MDN
- CSS Backgrounds and Borders Module Level 3:
border-radius
官方文档