笔记:SVG 环形动画(进度条)原理

1,690
原文链接: moxo.io

第一步,圆形的 SVG shape - <circle>

CodePen 链接
  1. SVG 中基本的形状(basis shape)有 circleellipselineploygonpolylinerect,环形进度条的展示在这里使用的是 circle;
  2. SVG 使用 x, y, width, height 来定义自身在页面上的 viewport,配合 viewbox 属性,来对内部子元素的单位进行计算,以上面 circle 为例,( svg 的 viewport 中 width / viewbox 中的 width )等于 200 / 200 = 1,这里是故意设置,不等于 1 的情况比比皆是,而且需要明确这里 1 是不带单位,是一个计算出的比例值。更多参考:《SVG 快速入门 - 基本概念》;
  3. circle 元素上的 cx、cy 属性分别指定 circle 的位置,r 代表 circle 的半径距离,例子中外层的 svg 的 width 和 height 都是 200,circle 的 cx 和 cy 都是 100 所以 circle 正好居中,同时 r 半径为 80;
  4. stroke 和 stroke-width 分别代表描边颜色和描边粗细,fill 代表 circle 整个的填充色, 例子中为无。

第二步,描边点状化

CodePen 链接
  1. stroke-dasharray 属性,可以将图形的描边进行「点状化」,这里需要理解的是,「点状化」的「点」,其大小是可以设置的,并不真的就是那么一个「·」,可以变长或者变短。上面例子中的三个 circle 分别设置 stroke-dasharray 为 10,50,100;
  2. 所以如果 circle 的点的长度正好等于 circle 边长,那么「点」看上去就是 circle 的边。

第三步,动起来(使用 stroke-dashoffset 抵消一个长度很长的「点」)

CodePen 链接
  1. stroke-dashoffset 可以使上一步中使用 stroke-dasharray 生成的「点」沿着 path 移动;
  2. 想象一个足够长的「点」+ 足够长的「offset」。比如 stroke-dashoffset 设置成 1000,元素的描边就不会显示,上面的例子中,三个 circle stroke-dasharray 分别为 100,500,1000,stroke-dashoffset 都是 1000,配合 animation 从 1000 运动到 0;
  3. circle 描边的 start point 在 circle 在 3 点钟方向,使用 transform: rotate() 逆时针旋转 90 度使 start point 定位到 12 点方向。

第四步,根据半径计算周长

auto animation

CodePen 链接

control stroke-dashoffset using JavaScript

CodePen 链接

作为底部占位符的圆,使用另外一个描边变淡变细的 <circle> 元素(希望可以找到更优、比如不需要多加元素的方案)。


第一步提到 <circle> 会自带一个 r 的属性,指定当前 circle 的半径,2 * r * Math.PI 可以得到 <circle> 的周长,这个周长,代表了我们需要 stroke-dasharray 代表的「点」的长度,同时也是也是 stroke-dashoffset 的值,长度相互抵消,视觉上就看不到这个「点」。


为了配合 CSS 中 transition 中的各种 easing 效果,不要直接在 <circle> 元素上设置 stroke-dasharraystorke-dashoffset,以 CSS 中对应的值代替,同时 JavaScript 控制的时候使用 el.style.storkeDash = val 的方式而不是 el.setAttribute(),分别写作 el.style.strokeDasharrayel.style.strokeDashoffsetarrayaoffseto 都不需要大写。

同时由于 offset 值是通过 JavaScript 计算得到,考虑通过 JavaScript 设置元素的 transition 值,避免 page load 的过程中元素发生动画;或者 CSS 默认给一个绝对大的值。


第四部第二个根据 input[range] 来演示的例子中,input 的值从 0 到 100 变化,step 是 1, stroke-dashoffset 对应的变化的值的公式就是 len - (range_value / 100) * len

参考

衍生阅读