本文翻译自:《Do you really understand CSS radial-gradients?》所有第一人称口吻均指原作者本人,作者是 Microsoft Edge 开发人员体验团队的 PM,之前曾在 Mozilla 工作。
前言
2022 年 10 月 24 日
七年前,我自学了所有关于CSS 线性渐变的知识,从那以后,我一直想了解更多关于径向渐变的工作原理。我花了很长时间才找到一个借口来深入挖掘其背后的逻辑,很庆幸我做到了。
为了学习如何使用radial-gradient
CSS 功能,我尝试仅使用 CSS 绘制一双眼睛,仅使用一个元素 par eye,并且仅使用径向渐变。你可以看到是下面这个结果:
https://code.juejin.cn/pen/7159818547550486535
当然,这不是你见过的最好的人眼图(另外,它在 Safari 中无法正确渲染,请参阅浏览器支持部分中的原因),但它确实帮助我更多地了解径向渐变很有效,通过这篇文章,我希望你也能从中学到一两点。
语法描述
从专业领域上讲,radial-gradient
语法如下所示:
1 | radial-gradient(<shape> <size> at <position>, <color-stops>); |
- Shape:渐变的形状。这默认为椭圆,但也可以是圆形。渐变中定义的颜色将显示在该形状内的同心层中。
- Size:渐变形状的大小。这对应于 100% 色标所在的最外层同心圆形状。
- Position:渐变形状的位置。默认情况下,渐变形状在渐变框中居中,但你可以选择所需的任何位置。
- Color stops:要在渐变中使用的颜色列表。颜色应该从最里面到最外面列出。第一种颜色将从形状的中心开始。
让我们稍微说明一下,以帮助理解这一点:
在上面的例子中,我们绘制了一个圆形渐变。矩形是渐变框(我们稍后会定义),白点是渐变的位置,水平线是渐变射线(稍后也会定义),最外面的粗圆是渐变形状,灰色的内圈代表色标的位置。
现在,让我们更详细地定义这些不同的部分。
渐变框
与传统背景图像不同,渐变图像没有维度,它是无限的。赋予渐变可见维度的是它的渐变框,即显示它的区域。
通常,渐变框是应用背景的元素的边框框。这与显示背景颜色或背景图像的区域相同。
这是应用于元素的红色到蓝色径向渐变:
但是,你可以使用background-size
属性设置渐变框的大小,或者使用background-position
。
这是相同的红色到蓝色径向渐变,但增加了设置为元素宽度和高度的 50% 的background-size
,并且background-position
设置为left center
(没有背景重复,所以我们可以更容易地看到渐变框的位置):
因此,渐变框并不总是像渐变适用的 DOM 元素那样定位和调整大小。但为了简单起见,在本文的其余部分,我们将假设渐变框与它所应用的 DOM 元素完全匹配。
中心点
因为径向渐变是椭圆或圆形,所以它有一个中心点。
即使中心点默认位于渐变框的中心,也可以使用<position>
参数将其定位在您想要的任何位置。这是在radial-gradient()
函数的第一个参数中at
关键字之后的部分。
让我们看几个例子:
没有
at
关键字,所以没有定义位置。这意味着中心点默认为中心:radial-gradient(fuchsia, darkblue, black, cadetblue)
用关键字定义的位置:
radial-gradient(at top left, fuchsia, darkblue, black, cadetblue)
用绝对坐标定义的位置:
radial-gradient(at 150px 70px, fuchsia, darkblue, black, cadetblue)
如你所见,位置可以省略,也可以像background-position
属性一样定义:使用top
, right
, bottom
,left
关键字,或者使用两个px长度作为渐变框内的左侧和顶部偏移量。
结束的形状
现在我们有一个要绘制的框(渐变框)和渐变形状的中心点,就要说一下结束形状。
这种形状被称为结束形状,因为在大多数情况下,这是最外层的形状,包含用于渲染渐变不同颜色的所有其他同心形状。
举个例子:
radial-gradient(circle 120px at center center, red, aqua, lime, gold, pink)
现在,让我们突出显示中心点、结束形状和所有颜色停止的同心形状:
结束形状是最大的圆圈,用粗线突出显示。
渐变的各种颜色也是围绕同一个中心点的圆圈。
这里有趣的是,可以使用我们之前在高级语法描述中看到的<shape>
和<size>
参数来定义和调整结束形状。
Shape:这可以是
ellipse
或circle
,默认为ellipse
缺失时。Size:这定义了结束形状的大小,并且可以具有以下值:
- 对于椭圆,它可以是两个长度,一个是水平半径,另一个是垂直半径。
- 对于一个圆,它可以是一个长度,对于圆的半径。
- 它可以是以下关键字之一:
closest-side
,farthest-side
,closest-corner
,farthest-corner
,我们稍后会解释。 - 如果缺少,则默认为
farthest-corner
.
为了熟悉这种语法,这里有几个有效的径向渐变示例:
没有形状(默认为椭圆),
closest-side
大小:radial-gradient(closest-side, black, lime, fuchsia)
圆形,半径为 100px:
radial-gradient(circle 100px, black, lime, fuchsia)
椭圆,水平半径为 200px,垂直半径为 50px:
radial-gradient(200px 50px, black, lime, fuchsia)
虽然使用长度定义大小相当简单,但使用关键字需要解释。
Closest-side 最近的边作为终止位置
使用此关键字,结束形状将调整大小,使其与渐变框离其中心最近的边(或多条边)相接。
对于一个圆而言这很简单,浏览器渲染引擎会找到盒子的顶部、右侧、底部或左侧中哪个最接近中心点,找出距离,并将其用作结束形状的半径.
例如,如果我们想要一个位于渐变框内 100px/150px 的圆,并且如果我们想要它的大小使其与最近的边相接,就可以使用以下语法:
radial-gradient(circle closest-side at 100px 150px, white, white)
这将给我们以下结束形状:
盒子的左边距离中心 100px,这是距离所有边最近的,所以形状的半径是 100px。
对于椭圆来说就有点复杂,因为椭圆有两个半径:水平半径和垂直半径。
在这种情况下,椭圆的水平半径的大小、结束形状与左侧还是右侧相交,具体取决于离哪个最近。并且椭圆的垂直半径的大小、结束形状形状与顶部还是底部相交,都具体取决于离哪个最近。
例如,让我们在形状内定义一个以 200px/100px 为中心的渐变:
radial-gradient(closest-side at 200px 100px, white, white)
我们就得到了以下结束形状:
如上图所示,左侧和顶部分别是水平和垂直方向最接近该点的边。因此,它们是结束形状与closest-side
关键字相遇的边。
Farthest-side 最远的边作为终止位置
既然我们知道了closest-side
关键字的工作原理,就更容易理解farthest-side
关键字了。
浏览器渲染引擎不会寻找最近的一侧(或多个侧面),而是寻找最远的一侧(或多个侧面)。
这是一个与以前相同的圆形示例:
radial-gradient(circle farthest-side at 100px 150px, white, white)
结果是这样:
如你所见,我们甚至看不到整个圆,因为半径比以前大得多。
最近的角和最远的角作为结束
现在,将side 替换为corner,你应该会看到这两个关键字是如何工作的。
浏览器渲染引擎现在不再测量中心点和边之间的距离,而是测量到渐变框最近和最远角的距离。
例如,使用下面的渐变,我们应该得到一个穿过盒子最近角的圆:
radial-gradient(circle closest-corner at 70px 70px, white, white)
成功了!正如你在下面看到的,框的左上角最靠近形状的中心,所以这就是形状经过的地方:
最后,思考下这个渐变:
radial-gradient(circle farthest-corner at 70px 70px, white, white)
渐变框的右下角是距离结束形状的中心点最远的角落,因此形状通过的地方是这样:
关于这些关键字的另一件事:如果您没有为结束形状定义大小,则默认为farthest-corner
但这是为什么?
嗯,我的理解是这个值在大多数情况下会产生最好的结果,特别是如果形状位于中心时,使用farthest-corner
的话,渐变中的最后一种颜色可以精确定位在框的最远可见点,从而为你提供尽可能多的空间来显示渐变。
让我们以红色到蓝色渐变的例子来比较closest-side
和farthest-corner
之间的区别:
如你所见,在后一种情况下,渐变有更多的呼吸空间。不过话又说回来,这取决于你要达到的是什么效果。
渐变射线
渐变射线是一条半线,从中心点开始,水平延伸,向右延伸,如下图所示:
现在,这条线本身并不是很有趣,但是当浏览器的渲染引擎沿着渐变分布颜色时,它确实发挥了作用。
因为径向渐变可以是椭圆,所以它的水平和垂直尺寸可能不同。想想如果你想要一个渐变的颜色为 20 像素的红色,然后是 50 像素的绿色,那么 20 和 50 像素在哪里测量?
这就是渐变光线的用武之地。
在色标中定义的颜色被放置在渐变射线上,从中心点开始,向右无限延伸。
这条射线类似于线性渐变中的渐变线。
让我们看下面的例子:
radial-gradient(circle 300px at left center, fuchsia 100px, aqua 200px, white 300px)
第一种颜色,紫红色,将沿着渐变光线定位,中心点右侧 100 像素,浅绿色在 200 像素,白色在 300 像素(恰好是圆的半径):
基于此,渲染引擎只需要弄清楚如何绘制同心形状。对于圆来说很容易,因为半径是已知的:这就是色标所在的位置。
而对于椭圆,渲染引擎会做更多的数学运算。它知道结束形状的两个半径,它知道中心点,它知道水平半径,因为那是色标的位置。
由此,需要找到色标形状的垂直半径。
这是一个示例,与上一个类似,但使用的是椭圆形状:
radial-gradient(300px 150px at left center, fuchsia 100px, aqua 200px, white 300px)
以下是色标沿渐变光线的分布方式,以及同心椭圆的外观:
色标
这将我们带到了径向渐变语法的最后一部分:Color stops 色标。
提醒一下,这是出现在以下表达式的形状、大小和位置之后的部分:
radial-gradient(<shape> <size> at <position>, <color stops>)
到目前为止,我们已经在本文中看到了许多色标示例,因此色标可以通过以下方式表示也就不足为奇了:
- 只是一种颜色,例如
red
. - 是颜色和位置,例如
red 100px
。
但是还有第三种方式:
- 颜色、开始位置和结束位置,例如
red 100px 200px
。
当没有定义位置时,这意味着我们让浏览器决定将颜色放在哪里。它将通过沿渐变光线均匀分布来实现这一点,具体取决于其他颜色。
当只定义一个位置时,那就是颜色应该出现的位置。
当定义了两个位置时,颜色应该从开始位置跨越到结束位置。
接下来让我们回顾几个例子。
Auto-distribution 自动分发
思考以下径向渐变:
radial-gradient(circle, red, blue, green, yellow)
在这里,我们没有定义任何位置。但是我们希望浏览器自动分配色标,它会很乐意这样做。
为此,它将第一种颜色放置在渐变射线上的 0% 处(即在中心点),然后它会将最后一种颜色放置在光线与结束形状相交的位置(即 100% 位置),最后它将其余的颜色均匀地分布在这两个边界之间。
因此,我们最终会得到:
- 0% 时为红色。
- 蓝色为 33.333%。
- 绿色为 66.666%。
- 黄色为 100%。
你能猜到为什么黄色在渐变框之外吗?
那是因为我们没有指定形状的大小,所以它默认为farthest-corner
,这意味着 100% 的位置实际上稍微在框外。但这也意味着角落完全是黄色的。
到目前为止,色标的自动分配似乎很简单不是吗?但有一些情况会变得更加复杂。思考以下径向渐变:
radial-gradient(circle, red 50px, blue, yellow 100px, aqua, lime, white 300px)
我们有以下站点:
- 50px 的红色
- 蓝色的
- 黄色 100px
- 水色
- 酸橙
- 白色 300 像素
一些有位置,有一些则没有写位置。
在这种情况下,浏览器将获取它所拥有的,并找出其余的。
要做到这点有一种方法:找到从一个定位的色标到下一个色标的范围,并在该范围内均匀分布非定位色标,以此类推。
在我们的例子中,意味着考虑红色到黄色的范围(从 50 到 100 像素),并找到蓝色的位置。因为那只是一个色标,所以把它放在范围的中心,75px。
然后,取黄色到白色的范围(从 100 到 300 像素),并在这个范围内(166.666 像素和 233.333 像素)均匀分布浅绿色和石灰。
我们最终得到以下渐变:
还有更多的极端情况,例如:
- 如果我们只有一个定位色标。
- 如果第一个定位色标有负的定位。
- 如果最后定位的色标位置大于 100%。
不过,我们不要在这上面花更多时间。希望这足以理解自动分发的工作原理。
尝试让浏览器找出你的颜色的最终分布!
Hard-stops 硬停止
使用色标可以显示彼此相邻的颜色,这非常有用,因为它们之间没有过渡。
是的,这与使用渐变的整个想法有点相反,其中颜色应该从一种平滑过渡到另一种,但它会产生非常有趣的效果。这里有一些例子:
当两个相邻的颜色停止共享相同的位置时,就会发生硬停止。在上图中的左上角示例中,红色圆圈可以通过以下方式完成:
radial-gradient(red 250px, white 250px)
实现硬停止的另一种方法是使用范围,如右上角的示例:
radial-gradient(lime 0 5vmin, #ffe88e 5vmin 10vmin, red 10vmin 15vmin, black 15vmin 20vmin, #21a3f3 0)
在此示例中,每种颜色都有两个位置:开始位置和结束位置。下一个颜色从前一个颜色结束的地方开始。
乱序色标
当然,如果总是按照出现的顺序给出色标,那就太容易了。
CSS 是如此美妙的语言,即使你搞砸了代码,它也总是知道该如何显示某些东西。实际上你可以用错误的顺序提供颜色色标,浏览器仍然会尝试找出一些显示方法。
例如以下渐变:
radial-gradient(rebeccapurple 250px, #000 100px, white 350px)
在这种情况下会发生什么?浏览器将从 250px 开始,然后意识到下一站位于 100px,这是错误的。应该在增加的位置给出止损。因此浏览器在这里会自动更正位置并将其设置为与上一个停靠点相同的位置:250px。然后它将继续变黑,定位在 300px。
我们最终会得到以下渐变:
这种纠错浏览器机制实际上可以派上用场。假设您想绘制圆圈作为元素的背景,但您希望这些圆圈的外部是透明的,以便其他圆圈也可见。
您可以将每个径向渐变的最后一站设置为transparent 0
。浏览器会将 0 更正为与上一站相同,因此在形状边缘创建硬限制,如下所示:
上图是在同一个元素上使用 5 种不同的径向渐变创建的,每种都有两个色标,一个是圆圈的颜色,另一个是transparent 0
. 这是完整的代码:
1 | .bubbles { |
超出范围的位置
我想给出的最后一个色标示例是超出范围的位置。到目前为止,我们已经看到了渐变光线的 0 到 100% 之间的颜色停止位置。但是,没有什么能阻止我们在这个范围之外设置位置。
例如以下渐变:
radial-gradient(circle at left center, blue -200%, red 200%)
这对于创建更微妙的渐变很有用。因为这里两个色标相距很远,并且在渐变框之外,我们只能看到红色到蓝色渐变的部分被放大:
浏览器支持
最后但同样重要的是,让我们快速浏览一下浏览器对radial-gradient
CSS 函数的支持:
总体而言,该功能在浏览器中得到了很好的支持,除了at
在 Safari 中用于在渐变框中定位结束形状的关键字。
如果你真的需要在 Safari 上使用此语法,那么你唯一的方法就是使用该background-position
属性将渐变移动到正确的位置。
现在,在我们结束之前,让我们看看一些通过使用径向渐变来获得有趣效果的演示:
https://code.juejin.cn/pen/7159846125581631520
*注意:最后一个在 Safari 上不起作用,请参阅浏览器支持*。
虽然这些纯粹是装饰性的例子,不太可能在真实的网站上有用,但 CSS 渐变(径向、线性和圆锥)在绘制元素背景中的细节时提供了很大的灵活性,而并不需要在网页文档中添加过多的元素。
希望我分享的这篇文章对你有所帮助或启发。