谢夏戈 @ xiexiage.com

SVG 动画

Aug 27, 2023

谢夏戈 @ xiexiage.com

今天的主题:动画 SVG Logo 👆

这个主题来源于 antfu(托尼) 的一篇文章:Animated SVG Logo
文中托尼大佬已经讲得很清楚了。所以我在这进行补充基础知识和实操记录。

SVG

SVG 是一种图片格式,矢量图。它跟其他图片格式的区别就在于:它可以无限放大,不会失真、丢失细节。

我们在 VSCode 编辑器中打开 SVG ,它不像 PNG、JPG 那样打开后直接是图片,而是一堆看不懂的乱码?(是 XML 格式的编码)
👇 左边是 PNG ,右边是 SVG 。

Animated-SVG-Logo-Animated-SVG-Logo-svg1

如果你要看 SVG 呈现图片模样,可以用浏览器打开,或者用一些查看图片的软件。

SVG 的编码格式是 XML ,我们先从 XML 讲起吧。👇

XML

  1. XML 和 HTML 很像,都是尖括号的标签。
  2. HTML 的标签都是有专门的作用,例如 a 标签是链接,h 标签是标题,img 标签是图片。而 XML 是没有特定规定的,你想怎样安排都可以,例如 👇
<title date="2023-08-24">介绍xml</title>
<author>xxx</author>
<date>2023-08-24</date>
<yyyy>2023</yyyy>
<mm>08</mm>
<dd>24</dd>

是的,尖括号里英文你可以自己写,你可以自定义标签,也可以自定义属性。xml 就是这么简单!互联网早期还用 xml 这个格式来传送/交换数据。现在改用 JSON 了。

你可以理解 HTML SVG 都是 XML 一种设定好的规则。


简单讲完 XML ,回到 SVG 。我们来看看 SVG 规定了哪些规则!👇

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="#999"
  stroke-width="10" fill="#222" />
</svg>

最外层 <svg><html> 一样包裹着其他标签。

里面的标签和属性就是画图用的规则了,像上面用到的 <circle> 标签就是用来规定画一个怎么样的圆。

  • cx 和 cy 是圆心坐标
  • r 是半径
  • stroke 是画笔颜色
  • stroke-width 是画笔粗细
  • fill 是填充这个圆的颜色

你可以把以上代码复制到一个后缀为 .svg 的文件里。然后用浏览器打开,就可以看到如下图案。

如果只是画圆圈,那还是比较简单的。但是如果要画精细一点的图案,难度不亚于用 0 和 1 写电脑程序…

  1. 所以我们不可能真的用 SVG 来画图,只是稍微了解一下它的原理,以便后续操控它。
  2. 我们应该用其他软件来画图,比如 Figma ,然后导出成 SVG 。

刚刚上面介绍的是用 SVG 画圆的一些标签和属性的规则。
接下来我们直奔主题,我们本次制作这个会动的 SVG 需要用到的是 SVG 路径!

是的!SVG 除了可以画圆,还可以画正方形、长方形、三角形、乱七八糟形。最重要的是今天的主角:路径,你可以理解为“一条线”

要了解更多 SVG 的画图规则,可以看看我写的小黄鸭:SVG | 小黄鸭

SVG 路径 - path

我们先来看看 SVG 画路径是怎么样的吧 👇

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <path d="M150 0 L75 200 L225 200 Z" />
</svg>

这个 <path> 标签,就是【路径】,里面的字母和数字也是有规则的 👇

每一个字母代表着一种操作:

  • M 代表移动画笔到某个位置
  • L 代表画一条直线到某个位置

… 这里就不过多介绍,毕竟我们不打算用 SVG 画图。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <path d="M150 0 L75 200 L225 200 Z"
    stroke='red' stroke-width='2' fill='none'/>
</svg>

我们同样可以使用 strokestroke-width 来规定画笔的颜色和粗细。

我们现在通过 Figma 来画一个图吧 👇

Animated-SVG-Logo-Animated-SVG-Logo-svg2

这里简单的用钢笔工具勾勒了一个 关于我的符号(戈),👆 你可以点击右下角的【Export】导出 SVG 图片 或者点击右上角切换模块就可以看到 SVG 的格式了 👇

Animated-SVG-Logo-Animated-SVG-Logo-svg3

它的 SVG 格式如下 👇

<svg xmlns="http://www.w3.org/2000/svg" width="115" height="103" viewBox="0 0 115 103" fill="none">

<path d="M3 46C66 27 35.5 -2 20.5 5.08248C-6.70896 17.9296 48.5 84.8205 80.5 69.5825C104.5 55.5 83 22.5825 56 36.5825C25 54 27.5 97.9226 69 99.5051C102 98.5 112 72 112 72" stroke="white" stroke-width="6" stroke-linecap="round"/>
</svg>

为了适配暗黑模式可以 👇

<svg ma xmlns="http://www.w3.org/2000/svg" width="115" height="103" viewBox="0 0 115 103" fill="none">
<style>
  path {
    stroke: #eee;
  }
  @media (prefers-color-scheme: dark) {
    path {
      stroke: #222;
    }
  }
</style>
<path d="M3 46C66 27 35.5 -2 20.5 5.08248C-6.70896 17.9296 48.5 84.8205 80.5 69.5825C104.5 55.5 83 22.5825 56 36.5825C25 54 27.5 97.9226 69 99.5051C102 98.5 112 72 112 72"  stroke-width="6" stroke-linecap="round"/>
</svg>

图片我们已经导出来了。但是应该如何动起来呢?

现在这个 SVG 图片是静止不动的,不过好在我们前面了解了一点关于 XML 和 SVG 的基础知识。我们知道 XML 是类似 HTML 的东西,所以我们也可以使用 CSS 也就是 <style> 标签来制作动画的。

我们先把 SVG 导入到一个 index.html 文件里,然后来了解更多关于路径的知识吧

SVG 路径 - 虚线

虚线:stroke-dasharray

我们在之前的(戈)svg 图片里加入虚线👇

<style>
  /* 修改 `<path>` 标签的 CSS */
  path {
    stroke-dasharray: 10px 10px;
    //参数1:虚线长度,参数2:虚线间隙
  }
</style>

矢量位移:stroke-dashoffset

path {
  stroke-dasharray: 10px 10px;
  stroke-dashoffset: 10px;
}

你可以把 stroke-dashoffset 的值依此修改成 20px、30px、40px
你就会发现这条线在“走”

因此我们就可以用 css 来做动画 👇

如果你不了解 CSS 动画,可以看我的小黄鸭教程: CSS 动画 | 小黄鸭

path {
  stroke-dashoffset: 0px;
  stroke-dasharray: 10px 10px;
  animation: grow 4s infinite;
}
@keyframes grow {
  0% {
    stroke-dashoffset: 0px;
  }
  50% {
    stroke-dashoffset: 100px;
  }
  to {
    stroke-dashoffset: 0px;
  }
}

我们利用 css 的动画改变 stroke-dashoffset ,它的虚线长度、虚线的间隔不变,只有矢量在动。

我们换个思路,改变虚线的长度!让间隔尽可能的长,这样在 SVG 这个区域内就只有一段虚线!而且是可以变长变短的虚线!

<svg
  xmlns="http://www.w3.org/2000/svg"
  width="645"
  height="383"
  viewBox="0 0 645 383"
  fill="none">
  <path
    d="M5 377.855C293 203.455 427 38.8546 285 8.85462C143 -21.1454 -122 131.855 78 194.855C132 211.865 378 263.855 640 126.855"
    stroke="white"
    stroke-width="10"
    stroke-linecap="round" />
</svg>
<style>
   path {
    stroke-dashoffset: 1px;
    stroke-dasharray: 100px 1000px;
    animation: grow 4s ease forwards infinite;
  }
  @keyframes grow {
     0% {
      stroke-dasharray: 0 500px;
      opacity: 0;
    }
    10% {
      opacity: 1;
    }
    40% {
      stroke-dasharray: 500px 0;
    }
    75% {
      stroke-dasharray: 500px 0;
    }
    95%,
    to {
      stroke-dasharray: 0 500px;
    }
  }
</style>

这样就完成了 SVG 的动画啦!✌

🥳 谢谢大家!


也许你会有疑问?为什么要用 SVG 来做动画而不是 GIF ?
其实是因为 SVG 可以无损放大,方便控制大小,更加适合用来做小图标,而且嵌入到网页中,也很方便配合暗黑模式 - DarkMode。

2023-PRESENT © 谢夏戈