背景
最近在做移动端组件库开发,UI出一个loading的设计稿,渐变空心圆,实现过程比较曲折,记录一下。
备注:全文基于react实现,可以自行翻译成其他框架版本。
实现思路
渐变首先想到的是css3属性linear-gradient,如下代码
<div className="circle" />
.circle {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(to bottom, #333, #fff);
}
效果:
好像跟UI稿差距有点大,中间需要挖空,最简单的办法,中间再放一个圆形div。
<div className="circle">
<div className="inner-circle" />
</div>
.circle {
width: 100px;
height: 100px;
padding: 10px;
border-radius: 50%;
background: linear-gradient(to bottom, #333, #fff);
}
.circle-inner {
width: 100%;
height: 100%;
background-color: #fff;
border-radius: 50%;
}
效果:
好像有点像了,但是这个渐变只能一个方向,跟UI稿的环形渐变还有差距,半个实现了,按照这个思路可以画2个半圆合在一起,这时候就需要3个圆。
<div className="circle">
<div className="circle-left" />
<div className="circle-right" />
<div className="circle-inner" />
</div>
.circle {
position: relative;
width: 100px;
height: 100px;
}
.circle-left {
position: absolute;
top: 0;
left: 0;
width: 50px;
height: 100px;
border-radius: 100px 0 0 100px;
background: linear-gradient(to bottom, rgb(51, 51, 51), rgba(51, 51, 51, 0.5));
}
.circle-right {
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 100px;
border-radius: 0 100px 100px 0;
background: linear-gradient(to top, rgba(51, 51, 51, 0.5), rgba(51, 51, 51, 0));
}
.circle-inner {
position: absolute;
top: 10px;
right: 10px;
width: 80px;
height: 80px;
border-radius: 40px;
background-color: #fff;
}
效果:
这个时候已经很接近UI的效果了,但是底部的连接处不是很完美,明显左边颜色太深了,把连接处的透明度调淡一点效果会比较好,这是左边调成background: linear-gradient(to bottom, rgb(51, 51, 51), rgba(51, 51, 51, 0.2));,右边调成background: linear-gradient(to bottom, rgb(51, 51, 51), rgba(51, 51, 51, 0.25));。
效果:
加上动画转起来效果会更好。
这样就实现了一个渐变空心圆,但是发来发现有一个致命的问题,linear-gradient不支持颜色+透明分开处理,导致无法轻松的修改颜色,通过查阅资料发现可以使用svg实现。
svg实现思路
跟纯css的实现思路相似,利用linearGradient画两个半圆,合并在一起,这里就直接贴代码了,感兴趣可以深入了解一下每一个api的作用,或者在评论区讨论。
<svg
width="240"
height="240"
viewBox="0 0 240 240"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
color="#666"
>
<defs>
<linearGradient id="linear-gradient1">
<stop offset="0%" stop-color="currentColor" />
<stop offset="50%" stop-color="currentColor" stop-opacity="30%" />
</linearGradient>
<linearGradient id="linear-gradient2">
<stop offset="50%" stop-color="currentColor" stop-opacity="30%" />
<stop offset="100%" stop-color="currentColor" stop-opacity="0" />
</linearGradient>
<circle
id="semi-circle"
cx="120"
cy="120"
r="100"
stroke-width="10"
stroke-dasharray="314 1000"
fill="none"
/>
</defs>
<g>
<use href="#semi-circle" stroke="url('#linear-gradient1')" />
<use
href="#semi-circle"
stroke="url('#linear-gradient2')"
style={{
transform: "rotate(180deg)",
transformOrigin: "center"
}}
/>
</g>
</svg>
效果:
关键是利用stop-color="currentColor",可以轻易修改渐变起始颜色。
遇到的坑
- svg渐变需要先定义linearGradient,然后通过id使用渐变色,在Chrome上多个svg,使用重复id,始终会渲染第一个赋值的颜色,在Safari上可以正确的显示颜色。
解决办法: 每一个svg的linearGradient id通过js动态生成
写在最后
本文介绍了2种实现渐变空心圆的思路,推荐大家使用svg实现,兼容性好,并且可以自定义颜色,放在组件里使用很方便。点此预览,F12可以直接查看代码