打工人必备!实现JS控制的SVG渐变图标,让你的页面更加生动有趣

在我们开发界面时,有时候渐变的图像会相比固定颜色的图形更加富有层次感与有趣。熟悉css的同学都知道,我们可以通过样式让背景呈现一个线性的渐变图片,比如这样:

1
2
3
.simple-linear {
background: linear-gradient(blue, pink);
}

也可以通过裁剪背景颜色到文本的方式实现文本颜色渐变

1
2
3
4
5
.text-gradient {
background-image: linear-gradient(to right, orange, purple);
-webkit-background-clip: text;
color: transparent;
}

这些解决方案都是网络上随便都能搜到的,那么现在背景渐变有了,文本渐变有了,看上去我们似乎可以实现各种渐变了,但是我们还差一个非常常见的元素没有办法做到渐变,那就是svg图标。

目前网络上所能找到的所有关于svg渐变颜色的方案都是需要通过svg本身的配置来实现的。举个例子:

比如这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<svg
class="svg-gradient"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<defs xmlns="http://www.w3.org/2000/svg">
<linearGradient y2="0" x2="1" y1="1" x1="1" id="svg_1">
<stop stop-color="#ff0000" offset="0" />
<stop stop-color="#ffff00" offset="1" />
</linearGradient>
</defs>
<path
d="M12 2a9 9 0 0 0-9 9v11l3-3l3 3l3-3l3 3l3-3l3 3V11a9 9 0 0 0-9-9M9 8a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m6 0a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z"
fill="url(#svg_1)"
/>
</svg>

这看上去很美好,因为我们我们只需要把这段svg代码复制到我们的项目中,然后需要的时候引用一下就好了。

当然是可以,但是在实际开发中我们往往会遇到更加复杂的挑战。比如我们用的图标是在一个图标库组件中,我们没有办法去直接修改(比如 react-icons )。比如我们需要让图标在渐变色(选中状态)和单色(未选中状态)之间来回切换。

难道我要准备两个一样的仅仅颜色不一样的图标因为状态来选择使用哪个图标么?这很丑陋,且难以维护。

想象一下我们是如何使用单色图标的?仅仅配置 color=#<hex-color> 即可实现不同颜色的切换。为什么渐变色图标不能有类似方式?

我搜索了网络上所有的资料,但是我没有找到我想要的方法,因此我决定自己探索。

一个很少有人会注意到的事实是,<defs> 标签的定义作用域不是父级的svg节点而是整个文档流。因此我们可以跨多个svg声明共用一个svg定义。然后只需要通过固定的id就可以了。

具体方案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.svg-gradient {
fill: url(#my-gradient);
}
</style>

<svg width="0" height="0">
<defs xmlns="http://www.w3.org/2000/svg">
<linearGradient y2="0" x2="1" y1="1" x1="1" id="my-gradient">
<stop stop-color="#ff0000" offset="0" />
<stop stop-color="#ffff00" offset="1" />
</linearGradient>
</defs>
</svg>

<svg
class="svg-gradient"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
d="M12 2a9 9 0 0 0-9 9v11l3-3l3 3l3-3l3 3l3-3l3 3V11a9 9 0 0 0-9-9M9 8a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m6 0a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z"
/>
</svg>

只需要我们把第一个表示颜色定义的svg放在全局预先加载,然后给定义的渐变色起一个有意义的名字,然后我们就可以在任意地方直接在svg的样式上告知需要采用的fill颜色即可 fill: url(#my-gradient);

颜色的切换就变成了 fill 属性的切换,这样就和单色图标一样了。

需要注意的是需要给用于声明的svg的宽高设为0,不然浏览器会给一个默认的宽高,会影响整体的布局。

最终看下成品效果:

文章目录