SVG动画开发实例三:SVG描边动画(路径动画、线条动画)|小册免费学

4,300 阅读7分钟

SVG描边动画(stroke animation),也可以叫做"路径绘制动画"(path drawing animation),路径描边动画或路径动画(path animation)。其实现的效果类似描边一样,将图形绘制出来,即沿着线条绘制,因此也被称为"线条动画"(line animation)

SVG是一种XML格式的图形,和DOM类似,可以使用CSS动画(关键帧keyframes)实现各种动效。

使用图片或css可能也可以实现一些动画效果,但是,svg图形可以无限放大缩小,动画效果的实现相较也比较简单。比图片灵活,而结合CSS动画又比纯CSS动画强大。

描边动画原理(重要)

SVG描边动画相关的3个属性:

  • stroke:用来定义边框的颜色。

  • stroke-dasharray:定义dashgap的长度,分隔实线和间隔的值。实现边框虚线的效果,和CSS中 dash 的效果一样。如:stroke-dasharray:5, 5表示,按照实线为5,间隔为5的排布重复下去。如下图:

    stroke-dasharray:5 同样表示实线5,间隔5的虚线排列下去。 stroke-dasharray:5, 3, 10 表示实线5,间隔3,实线10,然后间隔5,实线3,间隔10,实线5...不断的循环填满线条。

  • stroke-dashoffset:设置dasharray定义的dash线条开始的位置。值为 number || percentage。百分数是相对于SVG的viewport。通常结合 dasharray 可以实现描边动画效果。

SVG线条描边动画是通过stroke-dashoffsetstroke-dasharray实现的,分为两个步骤:

  1. 通过 dasharray 将实线部分增加至全长。比如:一条 path 的长度为300,如果把 SVG 中 path 的 stroke-dasharray 的值设置为300,300,即表示这条 path 将会按照实线为 300,间隔为 300 的排布重复下去。所以默认的情况下,我们只会看到一条300长度的实线,间隔300的线段由于已经在画布外,所以是不可见的。

  2. 同时,通过 stroke-dashoffset 移动新增的实线部分,造成线段移动的效果。比如由: stroke-dashoffset:500 变为 stroke-dashoffset:0

如下,通过一个示例了解描边的实现过程:

一条长200的线条。

<svg x="0px" y="0px" width="300px" height="100px" viewBox="0 0 300 100" class="svg1">
  <line x1="20" y1="50" x2="220" y2="50" stroke="#000" stroke-width="1" ></line>
</svg>
.svg1{
   border: 2px solid red;
}
.svg1 line {
   stroke-dasharray:200,200;
   stroke-dashoffset:200;
}

因为 stroke-dasharraystroke-dashoffset 的值都是200,所以这条线段显示的是长为200的gap,没有线条。把 stroke-dashoffset 的值设置为0,dash线段就显示了出来,如下动图所示:

实际上,只要gap的长度大于线段的长度,并且dashoffset与gap长度相同即可,当然dash的长度最好与线段长度相同。最好的是四者均相同。

间隔的距离要为实线的长度

描边的动画的另一个实现思路是,通过调整,递增虚线中实线的长度,使其逐渐占满整条线段。这样就不需要涉及stroke偏移的调整。

通过这个原理,结合CSS的transitionanimation就可以轻松实现一个描边动画:

.svg1{
   border: 2px solid red;
}

/* js toggle class */
.svg1 line {
   stroke-dasharray:200,200;
   stroke-dashoffset:200;
   transition:all 2s linear;
}
.svg1.move line{
   stroke-dashoffset:0;
}
let body=document.getElementsByTagName('body')[0];
body.addEventListener('click',()=>{
   var svg1=document.querySelector('.svg1');
   svg1.classList.toggle('move');
});

通过点击切换class,实现描边的变化。

  • css animation
/* css animation */
.svg1 line {
    stroke-dasharray:200,200;
    stroke-dashoffset:200;
    animation: moveline 2s linear infinite;
}
@keyframes moveline {
    50% {
        stroke-dashoffset:0;
    }
    100%{
        stroke-dashoffset:-200;
    }
}

注:

对于描边动画,虽然要描边的总长度不变,但是可以通过改变虚线的长短实现绘制的快慢,这需要实线和时间的配合。原因在于同一时间内,线段短的肯定要比线段长的移动的快才能绘制完成(offset偏移的大小)。

为了优化效果,多个图形的路径动画,还可以通过延迟、调整执行时长等,实现更好的效果。

SVG CSS 描边动画

描边动画:stroke animation。或者路径绘制动画:path drawing animation,有时也称为路径描边动画,路径动画path animation

描边动画示例

完成一个如下的动画效果:

这个动画由一个颜色填充动画描边线条动画放大动画三个动画组成。

首先看一下svg图片的代码,由一个圆和对勾path组成,首先默认看一下效果

<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
  <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" stroke="black" />
  <path class="checkmark__check" fill="none" stroke="black" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
</svg>

viewBox的作用在于以完整,或以设置的视野,将图像呈现到svg中,方便调整svg的大小,而不影响看到的(显示的)图形。这一点可以从测试使用中体会到

原动画的初始状态为空(各个元素不显示),需要描边的动画是圆圈和中间的对勾。

初始需要把circle的fill为none,没有填充颜色,stroke-dasharrystroke-dashoffset 的值设置为 circle 的长度值。把 path 元素的 stroke-dasharrystroke-dashoffset 的值设置为 path 的长度值。关于长度值的获取可以使用getTotalLength(),下一小部分有介绍

/* 定义svg */
.checkmark {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    stroke-width: 2;
    stroke: #fff;
    box-shadow: inset 0px 0px 0px #7ac142; 
}

/* 初始化时圆 */
.checkmark__circle {
    stroke-dasharray: 157;
    stroke-dashoffset: 157;
    stroke: #7ac142;
    fill: none;
}
/* 初始化时对勾 */
.checkmark__check {
    transform-origin: 50% 50%;
    stroke-dasharray: 34;
    stroke-dashoffset: 34;
}

圆和对勾的线条描边动画,关键帧中目标状态把 stroke-dashoffset 设置为0:

@keyframes stroke {
  100% {
    stroke-dashoffset: 0;
  }
}

填充和缩放的动画:

@keyframes scale {
  0%, 100% {
    transform: none;
  }
  50% {
    transform: scale3d(1.1, 1.1, 1);
  }
}
@keyframes fill {
  100% {
    box-shadow: inset 0px 0px 0px 30px #7ac142;
  }
}

完整代码如下:

<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
    <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
    <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
</svg>
/* 定义svg */
.checkmark {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    stroke-width: 2;
    stroke: #fff;
    box-shadow: inset 0px 0px 0px #7ac142; 
    animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
}

.checkmark__circle {
    stroke-dasharray: 157;
    stroke-dashoffset: 157;
    stroke: #7ac142;
    fill: none;
    animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}

.checkmark__check {
    transform-origin: 50% 50%;
    stroke-dasharray: 34;
    stroke-dashoffset: 34;
    animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}

@keyframes stroke {
    100% {
        stroke-dashoffset: 0;
    }
}
@keyframes scale {
    0%, 100% {
        transform: none;
    }
    50% {
        transform: scale3d(1.1, 1.1, 1);
    }
}
@keyframes fill {
    100% {
        box-shadow: inset 0px 0px 0px 30px #7ac142;
    }
}

使用getTotalLength()获取svg图形元素的长度

可以使用JavaScript 的 API获取整条 path 的长度值,只需两行代码:

let path = document.querySelector('path');
let p_length = path.getTotalLength();

同样,circle的长度也可以使用getTotalLength()获取

let circle = document.querySelector('circle');
let c_length = circle.getTotalLength();

关于svg图形元素的长度,可以在AI的"文档信息"面板中看到:

path多路径的描边动画的处理

先看一下如下六边形的svg:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 300 200" xml:space="preserve">
<path  class="st0" fill="green" stroke="#000" d="M181.5,111l-32,18l-31.6-18.7l0.4-36.7l32-18l31.6,18.7L181.5,111z M181.7,111.4l0.3-0.1
	l0.4-37.3l-32.1-19l-32.5,18.3l-0.4,37.3l32.1,19L181.7,111.4z M118.8,73.9l31.5-17.8l31.1,18.4l-0.4,36.1l-31.5,17.8l-31.1-18.4
	L118.8,73.9z"/>
</svg>

添加如下css,实现描边动画:

.st0{
    stroke-dasharray: 670 670;
    stroke-dashoffset:670;
    animation: st0move 3s linear;
}
@keyframes st0move {
    0%{
        stroke-dashoffset:0;
    }
    100%{
        stroke-dashoffset: 670;
    }
}

可以看到动画效果实现的"很乱",

这是因为path路径中使用了多个图形路径,这样就不能控制路径的起始位置,使用描边动画会多条线一起变化,不能控制顺序。导致变化时很乱。

要控制path路径的顺序,解决办法就是拆分path中的多条线(d属性中设置的多路径分别拆分为多个单条路径)。

操作办法一个是通过代码直接拆开;或者在AI中,用钢笔工具一笔一笔照着轮廓勾画出路径,然后导出为path元素。

修改后如下:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 300 200" xml:space="preserve">
    <path  class="st0" fill="green" stroke="#000" d="M181.5,111l-32,18l-31.6-18.7l0.4-36.7l32-18l31.6,18.7L181.5,111z"/>
    <path  class="st0" fill="green" stroke="#000" d="M181.7,111.4l0.3-0.1
	l0.4-37.3l-32.1-19l-32.5,18.3l-0.4,37.3l32.1,19L181.7,111.4z"/>
    <path  class="st0" fill="green" stroke="#000" d="M118.8,73.9l31.5-17.8l31.1,18.4l-0.4,36.1l-31.5,17.8l-31.1-18.4
	L118.8,73.9z"/>
</svg>
.st0{
    stroke-dasharray: 670 670;
    stroke-dashoffset:670;
    animation: st0move 2s linear forwards;
}
@keyframes st0move {
    0%{
        stroke-dashoffset:0;
    }
    100%{
        stroke-dashoffset: 670;
    }
}

.st0:nth-child(2) {
    animation-delay: 1s;
}
.st0:nth-child(3) {
    animation-delay: 2s;
}

关于path中多个路径的情况,在实际使用中会比这个复杂的多,此处只是个说明。处理起来也比较烦人,可能需要结合其他动画原理,重点还是图形中的实现元素要合理,便于操作控制。

比如此处例子,中间的六边形不用变,围绕六边形的边只要一条闭合线条就可以,但是原svg代码用了很多条线,这样在移动和控制时,效果就会很不好看。

本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情