图解CSS:border-radius
CSS的border-radius
属性是属于 CSS Backgrounds and Borders Module Level 3 的一部分。随着CSS技术的不断变革,border-radius
除了我们熟悉的物理属性之外新添加了逻辑属性。而且border-radius
取值不同,绘制的圆角效果也会有所差异。当然,在很多开发者眼中,border-radius
已经是非常简单的一个特性,但试问一下,你真的理解了该特性呢:
border-radius
取不同值会发生什么?border-radius
取值为%
值时相对于谁计算?border-radius
嵌套会发生什么?border-radius
的半径重叠时会发生什么?
如果你不能非常明确的回答上面提到这几个问题,那么接下来的内容就值得你花时间阅读。
border-radius
基本特性
CSS中的border-radius
主要作用是用来给元素绘制圆角效果。它和border
属性类似,是一个简写属性,如果不使用简写属性的话,border-radius
属性可以拆分为出border-top-left-radius
、border-top-right-radius
、border-bottom-left-radius
和border-bottom-right-radius
四个属性。
不过随着 CSS逻辑属性 的出现,border-radius
的逻辑圆角属性由border-start-start-radius
、border-start-end-radius
、border-end-start-radius
、border-end-end-radius
组成。不过,CSS逻辑圆角属性会受writing-mode
、direction
、text-orientation
属性的影响。
CSS的物理圆角和逻辑圆角有相应的映射关系:
来看一个简单地示例:
尝试着调整上面示例中不同值的设置,在不同的场景中看到的效果会不一样:
特别声明,为了不给大家带来概念上的混淆,接下来的内容都将围绕着物理圆角来展开。
CSS的border-radius
取值和我们熟悉的其他CSS属性(比如margin
、padding
)还是有所差异的。因为border-radius
可以取分x
轴和y
轴取值,而且每个轴取值个数可以是{1, 4}
(一到四个)。
border-radius: <length-percentage>{1,4} [ / <length-percentage>{1,4} ]?
而且border-radius
的x
和y
轴之间必须得由/
来分隔。如果在border-radius
中未显式使用/
分隔符,则表示x
轴和y
轴的值相等。
注意,使用
/
符来分隔x
轴和y
轴的半径值时,/
前后最好有一个空格。
单个值
让我们从最基础开始。希望这不会让你感到厌烦。毕竟CSS的border-radius
已经存在好几年了。
当你给border-radius
只显式设置一个值时,元素所有的角都会用这个值作为圆角半径:
.element {
border-radius: 120px;
}
正如上图所示,border-radius
取一个值时,除了四个圆角半径相同之外,而且x
轴和y
轴的都相同,即:
.element {
border-radius: 120px;
}
// 等同于
.element {
border-radius: 120px / 120px;
}
两个值
border-radius
取两个值时有两种场景:
.element {
border-radius: 60px 120px;
}
第一个值表示左上角和右下角圆角半径,第二个值表示右上角和左下角圆角半径。相当于:
.element {
border-top-left-radius: 60px;
border-bottom-right-radius: 60px;
border-top-right-border: 120px;
border-bottom-left-radius: 120px;
}
另外一个场景就是x
轴和y
轴的值不同:
.element {
border-radius: 60px / 120px;
}
上面这个代码同样表示的是元素所有圆角效果相同,只不过每个圆角的x
轴半径为60px
,y
轴的半径为120px
:
三个值
border-radius
取三个值的组合场景相对来说更为复杂一些,可以是:
border-radius: 40px 60px 80px;
border-radius: 40px 60px / 80px;
border-radius: 40px / 60px 80px;
这三种使用方式最终渲染效果都将会不一样:
你可能从上图中已经发现了他们之间的差异,这里我们着重来看border-radius: 40px 60px 80px
:
四个值
你可能可以感觉得到,随着取值个数越多,组合场景就会越多,比如四个值:
border-radius: 40px 60px 80px 100px;
border-radius: 40px / 60px 80px 100px;
border-radius: 40px 60px / 80px 100px;
border-radius: 40px 60px 80px / 100px
上面不同的值带来的效果也是不一样的:
八个值
我们跳过其他几个取值的演示,直接来看border-radius
取八个值效果。
.element {
border-radius: 30px 40px 50px 60px / 60px 50px 40px 30px;
}
就上面的示例而言,会令元素的每个圆角的x
轴和y
轴的半径不同,/
分隔符之前的值表示:
- 第一个值代表的是左上角
x
轴的圆角半径 - 第二个值代表的是右上角
x
轴的圆角半径 - 第三个值代表的是右下角
x
轴的圆角半径 - 第四个值代表的是左下角
x
轴的圆角半径
/
分隔符之后的值表示:
- 第一个值代表的是左上角
y
轴的圆角半径 - 第二个值代表的是右上角
y
轴的圆角半径 - 第三个值代表的是右下角
y
轴的圆角半径 - 第四个值代表的是左下角
y
轴的圆角半径
即:
border-radius: 30px 40px 50px 60px / 60px 50px 40px 30px;
/* 等同于 */
border-top-left-radius: 30px 60px;
border-top-right-radius: 40px 50px;
border-bottom-right-radius: 50px 40px;
border-bottom-left-radius: 60px 30px;
具体的效果如下:
简单地小结一下。
如果border-radius
取值中未显式地使用/
分隔符的话,则表示border-radius
的x
轴和y
的圆角半径相同。不管是x
轴还是y
轴取值都可以是 1~4
值,就此方面和我们熟悉的margin
、padding
取值类似:
- 如果只有一个值,则表示所有圆角的半径都相同
- 如果有两个值,则第一个值是左上角、右下角的圆角半径,第二个值是右上角和左下角的圆角半径
- 如果有三个值,则第一个值是左上角的圆角半径,第二个值是右上角和左下角的圆角半径,第三个值是右下角的圆角半径
- 如果有四个值,则第一个值是左上角的圆角半径,第二个值是右上角的圆角半径,第三个值是右下角圆角半径,第四个值是左下角圆角半径
上面这个取值规则适用于x
和y
的取值方式。
CSS border-radius
除了能绘制不同的圆角效果之外(比如上面示例所示),而且还可以绘制一些图形(CSS实现一些视觉效果):
border-radius
中的单位
在上面示例中我们演示的Demo,给border-radius
设置的单位都是px
单位(一个固定单位)。事实上border-radius
属性值的单位可以是<length-percentage>
,即<length>
或<percentage>
。
可以用于<length>
的单位有:
- 固定单位:
px
、pt
,还有不太常用的cm
、mm
、in
和pc
等 - 相对单位:
em
、rem
、ex
、ch
、vw
、vh
、vmax
、vmin
等,其中em
、rem
、ex
和ch
是相对于font-size
计算,vw
、vh
、vmax
和vmin
是相对于视窗计算
<percentage>
就是我们熟悉的%
单位。
相比而言,%
的取值是最为复杂的。特别是对于初学者而言,可能不太了解border-radius
值为%
时,它是相对于谁做计算。 如果你对这方面也不太清楚的话,那么可以接着往下阅读,如果你对这方面了解的话,可以跳过这节,继续下一节的内容。
在border-radius
使用%
值时,它计算的相对值是需要分开来算的,其中 x
轴的%
值相对于元素的width
值计算;y
轴的%
值相对于元素的height
值计算,比如:
.element {
width: 300px;
height: 300px;
border-radius: 30% 70% 20% 40%;
}
上面示例中border-radius: 30% 70% 20% 40%;
对应的计算结果是:
左上角(top-left)⇒ border-top-left-radius: 30% ⇒ x = y = 300px x 30% = 90px
右上角(top-right) ⇒ border-top-right-radius: 70% ⇒ x = y = 300px x 70% = 210px
右下角(bottom-right)⇒ border-bottom-right-radius: 20% ⇒ x = y = 300px x 20% = 60px
左下角(bottom-left)⇒ border-bottom-left-radius: 40% ⇒ x = y = 300px x 40% = 120px
效果看上去像下图这样:
上面示例是元素width
和height
相等,如果元素width
和height
不相等时:
.element {
width: 600px;
height: 300px;
border-radius: 30% 70% 20% 40%;
}
这个时候,border-radius: 30% 70% 20% 40%;
对应的计算结果是:
左上角(top-left)⇒ border-top-left-radius: 30% ⇒ x = 600px x 30% = 180px; y = 300px x 30% = 90px
右上角(top-right)⇒ border-top-right-radius: 70% ⇒ x = 600px x 70% = 420px; y = 300px x 70% = 210px
右下角(bottom-right)⇒ border-bottom-right: 20% ⇒ x = 600px x 20% = 120px; y = 300px x 20% = 60px
左下角(bottom-left)⇒ border-bottom-left: 40% ⇒ x = 600px x 40% = 240px; y = 300px x 40% = 120px
效果看上去像下图这样:
如果元素的width
和height
相等,但border-radius
属性的值是一个带/
符号的八个值:
.element {
width: 300px;
height: 300px;
border-radius: 70% 30% 30% 70% / 60% 40% 60% 40%;
}
对应的计算如下:
左上角(top-left)⇒ border-top-left-radius: 70% 60% ⇒ x = 300px x 70% = 210px; y = 300px x 60% = 180px
右上角(top-right)⇒ border-top-right-radius: 30% 40% ⇒ x = 300px x 30% = 90px; y = 300px x 40% = 120px
右下角(bottom-right)⇒ border-bottom-right-radius: 30% 60% ⇒ x = 300px x 30% = 90px; y = 300px x 60% = 180px
左下角(bottom-left) ⇒ border-bottom-left-radius: 70% 40% ⇒ x = 300px x 70% = 210px; y = 300px x 40% = 120px
对应的效果如下:
同样的,如果元素的width
和height
值不同时,计算方式相似:
.element {
width: 600px;
height: 300px;
border-radius: 70% 30% 30% 70% / 60% 40% 60% 40%;
}
对应的计算如下:
左上角(top-left)⇒ border-top-left-radius: 70% 60% ⇒ x = 600px x 70% = 420px; y = 300px x 60% = 180px
右上角(top-right)⇒ border-top-right-radius: 30% 40% ⇒ x = 600px x 30% = 180px; y = 300px x 40% = 120px
右下角(bottom-right)⇒ border-bottom-right-radius: 30% 60% ⇒ x = 600px x 30% = 180px; y = 300px x 60% = 180px
左下角(bottom-left) ⇒ border-bottom-left-radius: 70% 40% ⇒ x = 600px x 70% = 420px; y = 300px x 40% = 120px
对应的效果如下图所示:
如果你感兴趣的话,可以尝试着拖动下面示例中每个点的滑块,查看元素圆角的变化:
在CSS的世界中,属性取值是
%
值的话,他的计算是复杂的,而且不同的属性计算时相对物也有所不同,如果你对这方面感兴趣的话,建议你花一点时间阅读《CSS中百分比单位计算方式》一文。
border-radius
嵌套会发生什么?
在Web开发过程中,在使用border-radius
的时候,有的时候会产生圆角嵌套的视觉效果:
发生圆角嵌套的场景一般会出现在:
- 带有边框的圆角场景
- 带有内距的圆角场景
- 同时带有边框和内距的圆角场景
比如:
<!-- HTML -->
<body>
<div></div>
<div></div>
<div></div>
</body>
/* CSS */
body > div {
margin: 5px;
width: 480px;
height: 480px;
box-sizing: border-box;
border-radius: 50px;
}
div:nth-child(1) {
background-image: linear-gradient(134deg, #3023ae 0%, #c86dd7 100%);
border: 30px solid #f90;
}
div:nth-child(2) {
background-image: linear-gradient(134deg, #3023ae 0%, #c86dd7 100%),
linear-gradient(134deg, #f90 0%, #f90 100%);
background-origin: padding-box;
background-clip: content-box, padding-box;
padding: 30px;
}
div:nth-child(3) {
background-image: linear-gradient(134deg, #3023ae 0%, #c86dd7 100%),
linear-gradient(134deg, #f90 0%, #f90 100%);
background-origin: padding-box;
background-clip: content-box, padding-box;
padding: 30px;
border: 30px solid #f36;
}
效果如下:
正如上面示例所示,它们都产生了圆角嵌套的效果:
CSS的border-radius
定义了元素外圆角,那么对于内圆角会有:
- 当
border-radius
的值大于border-width
时,会产生内圆角,并且内圆角的半么为border-radius - border-width
- 当
border-radius
的值大于padding
时,也会产生内圆角,并且内圆角的半么为border-radius - padding
上面这两种场景刚好对应的是上图中的左图和中间图,但除此之外,同时会出现border-radius
、border-width
和padding
时,会产生多重内嵌套圆角,如上图最右侧效果。
会不会产生内圆角,则取决于:
- 如果
border-radius
的值大于border-width
时,则会产生内圆角 - 如果
border-radius
的值大于padding
时,则会产生内圆角 - 如果
border-radius
的值和border-width
的值(或padding
值)相等或小于时,则不会产生内圆角
也就是说,当内圆角的产生时,内圆角的半径则是他们之间的差,即border-radius - border-width
、border-radius - padding
或border-radius - border-width - padding
。比如上例:
border-radius - border-width = 50px - 30px = 20px
border-radius - padding = 50px - 30px = 20px
border-radius - border-width - padding = 50px - 30px - 30px = -10px
你可以尝试着在下面的示例中调整border-radius
、padding
和border-width
的值,查看元素圆角上的变化:
如果border-width
和padding
每个方向取值不同,这个时候和border-radius
产生的差值也将会不一样,也会出现内圆角的x
轴和y
的半径不同,内圆角的效果也将会类似于border-radius
设置了x
和y
轴的半径,比如border-radius: 10px / 20px
。我们在上面的示例稍作调整,效果如下:
尝试着调整示例中的相关参数,你可以看到相关变化,比如下图所示,我将相应的圆角半径标注出来,从图中可以明白其中的变化:
从上图效果也不难发现,尽管border-width
(或padding
)以及同时设置这两个值,并且每个方向的值不同,如果border-radius
和它们产生的差值大于0
就会产生内部圆角,而且圆角的半径就等于其差值。
border-radius
的半径重叠时会发生什么?
在Web中有一些UI的风格看上去就像“胶囊”的外形:
我们常把这种UI的风格称作“胶囊UI”,这种“胶囊UI”常用于一些button
、checkbox
和radio
的元素上。
CSS实现这种胶囊UI的效果,为了能达到一劳永逸,时常给元素的border-radius
值设置为一个较大的值,比如999rem
、999vmax
之类的。这样做不管元素高度是多少,都可以实现胶囊UI的效果:
拖动示例中右下角改变元素大小,但圆角效果是相同的(类似于胶囊):
示例中:
.pill {
border-radius: 999vmax;
}
这行代码的意思,我想大家都懂,.pill
元素四个角的“圆角”半径都是999vmax
。这种方式很方便,因为这意味着我们不需要知道元素(矩形框)的尺寸,它也能正常的工作。不过,在某些边缘情况上,会遇到一些奇怪的行为。比如在上面的示例基础上稍作调整,就是把border-radius
的值设置为:
.pill {
border-radius: 100px 999vmax 999vmax 100px;
}
拖动右下角改变元素大小时,你会发现元素.pill
左上角和右上角虽然设置了border-radius
的半径值为100px
,但并没有圆角效果:
那么问题来了:
border-radius: 100px 999vmax 999vmax 100px
为什么左上角和左下角100px
并没有生效?
为什么border-top-left-radius: 100px
和border-bottom-left-radius: 100px
消失了?他们去哪了?
Let f = min(Li/Si), where i ∈ {top, right, bottom, left}, Si is the sum of the two corresponding radii of the corners on side i, and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box. If f < 1, then all corner radii are reduced by multiplying them by f.
具体的解释请看下图:
公式看上去令人感到困惑,甚至是令人头痛。但我们只需要记住一点:这个公式的目的是防止border-radius
(圆角半径)重叠。简单地说:
客户端(浏览器)本质上是在想:“按比例缩小所有半径(
border-radius
),直到它们之间没有重叠”!
我们来用简单的示例来阐述上述公式的一些基本原理,这样可以让大家更好的理解。
首先,它会计算矩形(元素)每条边的长度与与它接触的半径之和的比值:
元素每条边宽度 / (相邻圆角半径1 + 相邻圆角半径2)
比如元素.pill
设置的样式:
.pill {
width: 600px;
height: 200px;
border-radius: 400px;
}
就该示例而言,按照上面示提供的公式就可以“计算出.pill
元素每条边的长度与与它接触的半径之和的比率”:
然后将所有圆角的半径去乘以这些比值(每条边计算出来的比率值)中的最小值。上例中计算出来的比率值只有.75
和.25
,取更小的值 .25,那么计算出来的圆角半径值则是:
400px x .25 = 100px
我们元素.pill
的height
是200px
(最短的边长),计算出来的border-radius
刚好是height
的一半,即 100px。这也让我们实现了一个“胶囊”UI效果。
为了能了解的更清楚一些,我们回到前面有问题的示例中,只不过我们用400px
来替代999vmax
,比如:
.pill {
width: 600px;
height: 200px;
border-radius: 100px 400px 400px 100px;
}
同样根据上面的公式来计算出每边的比例:
Ratio » 元素每条边宽度 / (相邻圆角半径1 + 相邻圆角半径2)
Top » 600px / (100px + 400px) = 1.2
Right » 200px / (400px + 400px) = 0.25
Bottom » 600px / (400px + 100px) = 1.2
Left » 200px / (100px + 100px) = 1
四个方向最小的比率是 0.25
,那么所有指定圆角半径乘以这个比例:
Top-Left » 100px x 0.25 = 25px
Top-Right » 400px x 0.25 = 100px
Bottom-Right » 400px x 0.25 = 100px
Bottom-Left » 100px x 0.25 = 25px
这样一来,运用于.pill
元素的border-radius
值为25px 100px 100px 25px
:
是不是觉得非常的神奇。我想这部分内容能解答你平时碰到的一些怪异的现象,即 使用border-radius
怪异的现象!
小结
CSS的border-radius
已经存在很多年了,我想很多同学在平时的开发中都会使用它来实现一些圆角效果,而且还会使用它制作一些有趣的UI效果。但我想很多同学在使用border-radius
会碰到一些怪异的现象,比如文章中圆角的嵌套,圆角重叠时按比例缩小等。最后希望这篇文章对大家有所帮助,如果你在使用border-radius
时碰到其他一些怪异现象,欢迎在下面的评论中与我们共享。