使用 SVG 实现一个圆环
我们先来实现一个简单的 SVG 描边动画
首先画一条直线
html
<svg class="svg-container" x="0" y="0" width="300px" height="100px" viewBox="0 0 300 100">
<line class="line" x1="50" y1="50" x2="250" y2="50" stroke="#000" stroke-width="2"></line>
</svg>在做之前我们先了解下 SVG 描边动画密切相关的 3 个属性
stroke:图形元素的外轮廓的颜色stroke-dasharray:定义 dash 和 gap 的长度。其实就是类似 CSS 中 dash 效果。例如给上面直线stroke-dasharray: 20, 5;表示按照实现为 20,间隔为 5 的排列重复下去。如下图
stroke-dashoffset:用来设置dasharray定义 dash 线条开始的位置。
SVG 线条描边动画的原理
简单来说,就是通过 stroke-dashoffset 和 stroke-dasharray 来做。主要是以下两个步骤:
- 将
stroke-dasharray增加至全长 - 控制
stroke-dashoffset造成线段移动的效果
接下来我们来完成上面直线的描边动画
我们先获取上面的直线的长度
jsx
const pathLength = document.querySelector('.line').getTotalLength()
console.log(pathLength) // 200再给个样式
css
.svg-container line {
stroke-dasharray: 200, 200;
stroke-dashoffset: 200;
transition: stroke-dashoffset 2s linear;
}这表示这段直线以 200 的长度,200 的间隔排列重复下去,但直线开始的位置是线段的整长,所以这个时候我们是看不到这条直线的, 但是当我们改变 stroke-dashoffset 的时候,一个描边动画效果就完成来
大致了解了 SVG 动画的原理之后,我们实现圆环就简单了
html:
html
<svg class="svg" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 500 500">
<defs>
<pattern id="fill-img" patternUnits="userSpaceOnUse" width="500" height="500">
<image
xlink:href="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88d08134596f4887ba3e5ba83f7d8e44~tplv-k3u1fbpfcp-watermark.image"
x="70"
y="70"
width="360"
height="360"
/>
</pattern>
</defs>
<circle class="circle" cx="250" cy="250" r="158" fill="none" stroke="url(#fill-img)" stroke-width="44"></circle>
</svg>这里用到了 <pattern> 的标签,然后设置 circle 的 stroke 属性为该元素,注意下这里各个元素的大小,比如我这里用的圆环 图片大小为 360 x 360, 圆环的宽度大概为 44,所以设置 circle 的 stroke-width 为 44,半径为 (360 / 2) - (44 / 2) = 158
css:
css
.circle {
stroke-dasharray: 993, 993;
stroke-dashoffset: 993;
transition: stroke-dashoffset 2s cubic-bezier(0.09, 0.76, 0.47, 1);
}
.move {
stroke-dashoffset: 0;
}这里用 cubic-bezier() 函数 制作先快后慢的效果
js:
jsx
document.querySelector('.circle').classList.add('move')效果如下:
大致效果已经出来了,我们再来优化下
加个背景图
html
<div class="container">
<svg class="svg" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 500 500">
<defs>
<pattern id="fill-img" patternUnits="userSpaceOnUse" width="500" height="500">
<image
xlink:href="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88d08134596f4887ba3e5ba83f7d8e44~tplv-k3u1fbpfcp-watermark.image"
x="70"
y="70"
width="360"
height="360"
/>
</pattern>
</defs>
<circle class="circle" cx="250" cy="250" r="158" fill="none" stroke="url(#fill-img)" stroke-width="44"></circle>
</svg>
<img
class="bg"
src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0e9a28857be497c968035d4092bf182~tplv-k3u1fbpfcp-watermark.image"
alt=""
/>
</div>使用 transform 改变起点
css
.circle {
stroke-dasharray: 993, 993;
stroke-dashoffset: 993;
transition: stroke-dashoffset 2s cubic-bezier(0.09, 0.76, 0.47, 1);
transform-origin: center;
transform: rotate(90deg);
}
.move {
stroke-dashoffset: 0;
}
.container {
position: relative;
}
.svg {
position: absolute;
}
.bg {
position: absolute;
left: 70px;
top: 70px;
z-index: -1;
}至此一个帅气的圆环就完成了
我们也可以使用 anime 来做
jsx
import anime from 'animejs'
const rate = 0.8
const pathLength = document.querySelector('.circle').getTotalLength()
anime({
targets: document.querySelector('.circle'),
strokeDashoffset: [pathLength, pathLength * (1 - rate)],
easing: 'easeOutQuint',
duration: () => {
return 2000 * rate
},
})