Skip to content

使用 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>

CleanShot 2021-03-19 at 15.23.13@2x.png

在做之前我们先了解下 SVG 描边动画密切相关的 3 个属性

  • stroke:图形元素的外轮廓的颜色
  • stroke-dasharray:定义 dash 和 gap 的长度。其实就是类似 CSS 中 dash 效果。例如给上面直线 stroke-dasharray: 20, 5; 表示按照实现为 20,间隔为 5 的排列重复下去。如下图

CleanShot 2021-03-19 at 15.31.26@2x.png

  • stroke-dashoffset:用来设置 dasharray 定义 dash 线条开始的位置。

SVG 线条描边动画的原理

简单来说,就是通过 stroke-dashoffsetstroke-dasharray 来做。主要是以下两个步骤:

  1. stroke-dasharray 增加至全长
  2. 控制 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 的时候,一个描边动画效果就完成来

2021-03-19 16.04.06.gif

大致了解了 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> 的标签,然后设置 circlestroke 属性为该元素,注意下这里各个元素的大小,比如我这里用的圆环 图片大小为 360 x 360, 圆环的宽度大概为 44,所以设置 circlestroke-width44,半径为 (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')

效果如下:

2021-03-19 17.12.27.gif

大致效果已经出来了,我们再来优化下

加个背景图

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;
}

至此一个帅气的圆环就完成了

2021-03-19 15.04.22.gif

我们也可以使用 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
  },
})