让 svg 动起来吧!

3,729 阅读12分钟

前言

想必大家对 svg 肯定不会陌生吧,像平时用到的 icon 图标就是最简单的 svg 图形,但怎么样能让 svg 动起来呢?

那就从最基本的svg 构成开始开始说起吧!

svg

svg 它的英文全称为Scalable Vector Graphics,意为可缩放的矢量图形,是一种标准的图形文件类型,基于XML来描述二维图型和绘图程序的语言,可以在浏览器中直接显示,也可以在图像编辑软件中编辑和创建 在放大或改变图像尺寸的情况下其图形质量也不会有所损失。

与其他图像格式相比(比如jpge和 gif),使用 svg 的优势在于 :

  • svg 图像可以通过文本编辑器来创建和修改;
  • svg 图像可被搜索、索引、脚本化或压缩,能够与 Java 技术一起运行;
  • svg 矢量图在任何的分辨率下被高质量地打印;
  • svg 在图像放大、伸缩的情况下不失真;
  • svg 图像中的文本是可选的,同时也是可搜索的(很适合制作地图);
  • svg 是开放的标准,是纯粹的 XML

tips:

  • 矢量图:是通过组成图形的一些基本元素,如点、线、面,边框,填充色等信息通过计算的方式来显示图形的。常见的矢量图如 svg 、pdf
  • 位图又叫像素图或栅格图,它是通过记录图像中每一个点的颜色、深度、透明度等信息来存储和显示图像。位图适用于需要指定每个像素的确切颜色的照片等高度详细的图像。位图具有固定的分辨率,因此增加大小会降低图像的质量,常见的位图 png、jpge

svg 的用途

在开发的过程中 svg 的用途也较为广泛,小到网站的logo,大到大屏的数据可视化,作为矢量图,svg能够做到适应各个屏幕的展示而不失真。

  1. 图标 - icon

由于svg格式文件的简单性和明确定义的边界,大多数图标都可以很好地转换为矢量。按钮等页面元素的图标需要对不同的屏幕尺寸做出响应,这意味着这些图标必须具有完美的可扩展性。

推荐的图标库: iconfonticonparkFont Awesome

  1. logo

svg支持在任何分辨率下的高质量打印,所以svg格式文件特别适合以不同形式展并打印在任何商品上起到品牌的宣传效益。同样,logo的设计往往更简单,这很好地适用于svg格式文件。

  1. 插画

矢量也非常适合非照片视觉艺术。如果添加为svg格式文件,网页上的装饰图既可以轻松缩放,又可以节省文件空间。甚至是插画中形状的纹理,都可以使用svg格式文件来实现。

  1. 数据可视化(地图)

数据内容丰富的网站或应用程序需要借助信息图表或插图图表来传达数据信息,帮助用户清晰理解数据。svg格式文件可以将设计无缝扩展,将图表设计为基于实时数据输入动态更新的 svg。

  1. svg 动画

svg 图像可以通过 CSS 和 JavaScript 进行样式和交互的控制,使其更加灵活和多样化,实现动画、图表的动态交互。

正如我们所看到的,svg的应用几乎无处不在,高效、便捷的适用于各种场景。

svg 的语法

了解了以上那么多关于 svg 的理论与应用,光说不练假把式,

这一章节就让我们一起来实操一下,深入体会一把 svg 是如何绘制的吧!!!

svg 的实现效果可以在 菜鸟 - 在线svg编辑器 中在线的绘制。

svg - 标签

svg 的图像代码都在 <svg> 根元素下:

<svg width="100" height="100" viewBox="50 50 50 50">
  ...
</svg>
  • 标签中的 width、height 属性指定了 svg 图像所占据 html 元素的宽高比。

    • 除了相对单位,也可以采用绝对单位(px)

    • svg 图像默认大小300(px) x 150(px)

  • viewBox: 将展示指定范围内 svg 的部分图像

    • viewBox的四个参数分别是左上角的横、纵坐标、视口的宽、高

    • 视口必须适配所在的空间,视口小于图像时视口会放大、缩小相应的倍数去适配 svg 图像的大小

视口与空间的适配,当视口视口与 svg 的宽高一致时,所展现的内容是正常比例,

例如下图的矩形所在的位置及大小是正常的 100 × 100,不会被视口缩放

<svg width="200px" height="200px" viewBox="0 0 200 200"> 
 	<rect x="0" y="0" height="100px" width="100px"  fill="yellow" stroke="red" stroke-width="1" />
</svg>

如果视口的大小是 50 x 50,由于 svg 图像的大小是 100 x 100,所以视口会放大去适配 svg图像的大小,即放大了四倍。

<svg width="200px" height="200px" viewBox="0 0 50 50"> 
 	<rect x="0" y="0" height="100px" width="100px"  fill="yellow" stroke="red" stroke-width="1" />
</svg>

如果不指定width属性和height属性,只指定viewBox属性,则相当于只给定 svg 图像的长宽比。

这时,svg 图像的默认大小将等于所在的 HTML 元素的大小。

<svg  viewBox="0 0 100 100"> 
 	<rect x="0" y="0" height="100px" width="100px"  fill="yellow" stroke="red" stroke-width="1" />
</svg>

rect - 矩形

svg 中的 <react> 标签用于创建矩形

<svg width="200px" height="200px" viewBox="0 0 200 200"> 
 	<rect x="10" y="10" height="100px" width="100px"  rx="10" ry="10" fill="yellow" stroke="red" stroke-width="1" />
</svg>

react 绘制过程中常见的属性:

  • x、y:矩形开始的横、纵坐标
  • height、width:矩形的宽高
  • fill:图形的填充色
  • stroke: 描边颜色
  • stroke-width: 描边宽度

react 圆角设置

  • rx:圆角x方位的半径

    • rx属性的实际效果取决于矩形的 ry属性和宽度
  • ry:圆角y方位的半径

    • 如果 ry 和 ry 都没有被正确的赋值,那么浏览器会绘制一个带有直角尖角的矩形。

p - 路径

<p> 标签用于绘制路径

如下图从iconfont中下载的 svg 图标

<svg t="1683276478956" class="icon" viewBox="0 0 1034 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22808" width="128" height="128">
  <path class="icon-path" d="M796.409538 211.994252c-7.946996-1.175779-11.320836-4.843308-13.861706-12.178367-16.742313-48.327682-45.712111-88.251973-86.432535-118.928646-121.323183-91.397616-289.99372-61.353346-372.841613 66.125023-3.641947 5.604648-7.379061 6.818289-13.512758 6.77224-78.121242-0.590448-147.71732 50.928926-170.386622 125.362172-1.331321 4.371564-5.143137 9.231245-9.136078 11.365862C41.345664 338.041016-5.786797 431.595762 9.520841 530.927071c17.334807 112.485911 115.462708 195.990766 229.862201 195.180308 4.939499-0.034792 11.546987-2.408863 14.496156-6.03546 16.399505-20.168342 31.907711-41.061185 49.092092-63.495126-5.964852 0-9.741875 0.078795-13.513782-0.01228-25.487487-0.620124-51.221592 0.76134-76.41846-2.288113C123.225509 643.405819 58.980299 551.182394 79.021751 462.819904c14.056134-61.975517 52.141544-102.616122 112.443955-122.316813 6.583952-2.15099 10.252505-4.199649 10.352789-12.432147 0.909719-74.740238 76.205613-124.812657 145.456836-96.806813 8.319479 3.364631 12.099572 2.426259 15.017018-6.495948 8.136308-24.88476 21.825075-46.6249 39.251979-66.157769 70.907956-79.476099 199.910029-81.456197 273.16238-4.130064 30.506804 32.203446 48.833195 69.966515 52.519144 114.435309 0.636496 7.689123 2.054799 12.428054 12.196787 11.602246 15.517415-1.264806 31.42778-1.870604 46.813189 0.071631 106.217137 13.408381 180.512237 117.089765 159.665443 221.912136-16.767895 84.314291-82.44471 143.930063-169.095209 153.84897-4.106528 0.469698-8.876158 2.606361-11.707646 5.539156-17.691941 18.331507-34.964326 37.068244-52.240805 55.796794-1.980097 2.146896-3.121084 5.066389-5.27719 8.674567 18.21485 0 34.580586 0.274246 50.934043-0.050142 56.078203-1.113357 107.891266-15.993253 153.187915-49.844222 64.441685-48.15679 100.274798-112.348788 105.933681-192.933127 3.987824-56.780191-9.784854-109.213377-40.996716-156.48603C933.808862 262.176165 873.601618 223.413326 796.409538 211.994252z" p-id="22809"></path>
  <path class="icon-path-yellow" d="M706.563254 653.67265c-77.533864 0-155.067728 0-234.922451 0 79.116919-97.599875 156.589384-193.171558 234.060827-288.742217-66.042135 27.891234-130.48689 57.473993-194.833407 87.269599-3.046383 1.410116-5.689582 4.361331-7.734148 7.169283-63.380516 87.044472-126.666888 174.156482-189.926654 261.287935-1.372254 1.890047-2.26253 4.129041-4.349051 8.012488 81.116459 0 160.327522 0 241.365186 0-51.78748 88.22946-102.526071 174.673252-153.265686 261.117043 0.648776 0.457418 1.297552 0.915859 1.946328 1.373277 102.984513-111.606891 205.970048-223.213781 308.954561-334.820672C707.428971 655.450133 706.996113 654.561903 706.563254 653.67265z" p-id="22810"></path>
</svg>

  • d: 点集数列用于定义 path 路径绘制的顺序, 是一个命令+参数 的序列合集,它的值是一个长字符串
<svg width="300" height="180">
<path d="
  M 18,3
  L 46,3
  L 46,40
  L 61,40
  L 32,68
  L 3,40
  L 18,40
  Z
"></path>
</svg>

image.png

这里绘制了一个向下的箭头,其中d序列中的每个字母表示一个绘制动作,后面跟着绘制的坐标位置。

  • M:移动到(moveto)
  • L:画直线到(lineto)
  • Z:闭合路径

circle - 圆形

<circle> 绘制圆

<svg width="200px" height="200px" viewBox="0 0 200 200"> 
	<circle cx="100" cy="100" r="50" fill="yellow" stroke="red" stroke-width="1" ></circle>
</svg>

在绘制圆时

  • r 代表圆的半径大小
  • cx、cy 表示圆心所在的横、纵坐标

如果省略cx和cy,圆心的位置会被设置为(0, 0)

<svg width="200px" height="200px" viewBox="0 0 200 200"> 
	<circle r="50" fill="yellow" stroke="red" stroke-width="1" ></circle>
</svg>

ellipse - 椭圆

<ellipse> 绘制椭圆

<svg width="200px" height="200px" viewBox="0 0 200 200"> 
	<ellipse cx="100" cy="100" ry="40" rx="80" fill="yellow" stroke="red" stroke-width="1" ></circle>
</svg>

  • rx、ry:椭圆横向轴和纵向轴的半径大小
  • cx、cy:属性 rx、ry 与圆的属性一致为,制定了椭圆中心的横、纵坐标

line - 线

<line> 能够用来绘制直线

<svg width="200px" height="200px" viewBox="0 0 200 200">
  <line x1="50" y1="50" x2="200" y2="50" stroke="red" stroke-width="1"  />
</svg>
  • x1,y1:直线的起点坐标
  • x2,y2:直线的终点坐标

polyline - 折线

polyline 顾名思义与line标签相似,不同的是 <polyline> 能够实现折线的绘制,通过折线也能实现图形的闭合,如长方形的绘制:

<svg width="200px" height="200px" viewBox="0 0 200 200">
  <polyline  points="20,50 20,100 50,100 50,50 20,50" fill="yellow" stroke="red" stroke-width="1"  />
</svg>

image.png

  • points:指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。
  • 同样 polyline 也可以填充颜色

polygon - 多边形

polygon 多边形绘制,与折线类似,通过 points 定位每个点的坐标,根据坐标依次进行绘制。

例如菱形的实现:

<svg width="200px" height="200px" viewBox="0 0 200 200">
  <polygon fill="yellow" stroke="red" stroke-width="1" points="20,20 100,0 80,80 0,100 20,20"/>
</svg>

image.png

横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。

svg 的使用

  • svg 文件可通过以下标签嵌入 html 文档:<embed><object> 或者 <iframe>

  • svg 代码可以直接嵌入到 html 页面中

  • 使用 img 的 src 属性链接到文件中。

svg 的动画效果

下面将介绍三种方式来实现 svg 的动画效果:

<animate> 标签

通过 animate 标签可以实现图像的动态效果

<svg width="500px" height="200px" viewBox="0 0 500 200">
  <rect x="0" y="10" width="100" height="100" fill="yellow" stroke="red" stroke-width="1" >
    <animate attributeName="x" from="0" to="500" dur="2s" repeatCount="indefinite" />
  </rect>
</svg>

实现了方块在 2s 内 x 水平位置的位移变化,并实现动画的无限循环播放。

  • attributeName:发生动画效果的属性名。
  • values:设置目标属性在各个关键帧值的大小。
  • from:单次动画的初始值。
  • to:单次动画的结束值。
  • dur:单次动画的持续时间。
  • repeatCount:动画的循环模式。

可以定义多个属性的动画效果来实现更为复杂的动画效果。

结合多个 animate 实现位置与宽度的同时变化:

<svg width="500px" height="200px" viewBox="0 0 500 200">
  <rect x="0" y="10" width="100" height="100" fill="yellow" stroke="red" stroke-width="1" >
   	<animate attributeName="x" from="0" to="500" dur="2s" repeatCount="indefinite" />
	<animate attributeName="width" to="500" dur="2s" repeatCount="indefinite" />
  </rect>
</svg>

<animateTransform>标签

由于 <animate> 标签对 CSS 的 transform 属性不起作用,所以在需要对图像变形时,要使用<animateTransform> 标签实现动画效果。

<svg width="200" height="200" viewBox="0 0 200 200">
  <rect x="50" y="50" width="50" height="50"  fill="yellow" stroke="red" stroke-width="1">
    <animateTransform attributeName="transform" type="rotate" begin="0s" dur="10s" from="0 100 100" to="360 50 50" repeatCount="indefinite" />
  </rect>
</svg>

<animateTransform>的效果为旋转(rotate),

  • from、to:表示旋转的角度值以及旋转的坐标位置
    • from="0 100 100" 开始时,角度为0,围绕(100, 100)开始旋转;
    • to="360 50 50" 结束时,角度为360,围绕(50, 50)旋转。

css 中的 animation 属性实现动效

在 svg 中同样可以通过css样式来控制 icon 的颜色填充与样式描边,甚至可以结合 animation 实现 icon 的动画效果的绘制。

先来看一下我们将要实现的效果:

为 icon 描边后进行填充 首先我们可以从 iconfont 中挑选一个喜欢的 icon

<svg t="1683276478956" class="icon" viewBox="0 0 1034 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22808" width="128" height="128">
  <path d="M796.409538 211.994252c-7.946996-1.175779-11.320836-4.843308-13.861706-12.178367-16.742313-48.327682-45.712111-88.251973-86.432535-118.928646-121.323183-91.397616-289.99372-61.353346-372.841613 66.125023-3.641947 5.604648-7.379061 6.818289-13.512758 6.77224-78.121242-0.590448-147.71732 50.928926-170.386622 125.362172-1.331321 4.371564-5.143137 9.231245-9.136078 11.365862C41.345664 338.041016-5.786797 431.595762 9.520841 530.927071c17.334807 112.485911 115.462708 195.990766 229.862201 195.180308 4.939499-0.034792 11.546987-2.408863 14.496156-6.03546 16.399505-20.168342 31.907711-41.061185 49.092092-63.495126-5.964852 0-9.741875 0.078795-13.513782-0.01228-25.487487-0.620124-51.221592 0.76134-76.41846-2.288113C123.225509 643.405819 58.980299 551.182394 79.021751 462.819904c14.056134-61.975517 52.141544-102.616122 112.443955-122.316813 6.583952-2.15099 10.252505-4.199649 10.352789-12.432147 0.909719-74.740238 76.205613-124.812657 145.456836-96.806813 8.319479 3.364631 12.099572 2.426259 15.017018-6.495948 8.136308-24.88476 21.825075-46.6249 39.251979-66.157769 70.907956-79.476099 199.910029-81.456197 273.16238-4.130064 30.506804 32.203446 48.833195 69.966515 52.519144 114.435309 0.636496 7.689123 2.054799 12.428054 12.196787 11.602246 15.517415-1.264806 31.42778-1.870604 46.813189 0.071631 106.217137 13.408381 180.512237 117.089765 159.665443 221.912136-16.767895 84.314291-82.44471 143.930063-169.095209 153.84897-4.106528 0.469698-8.876158 2.606361-11.707646 5.539156-17.691941 18.331507-34.964326 37.068244-52.240805 55.796794-1.980097 2.146896-3.121084 5.066389-5.27719 8.674567 18.21485 0 34.580586 0.274246 50.934043-0.050142 56.078203-1.113357 107.891266-15.993253 153.187915-49.844222 64.441685-48.15679 100.274798-112.348788 105.933681-192.933127 3.987824-56.780191-9.784854-109.213377-40.996716-156.48603C933.808862 262.176165 873.601618 223.413326 796.409538 211.994252z" p-id="22809"></path>
  <path d="M706.563254 653.67265c-77.533864 0-155.067728 0-234.922451 0 79.116919-97.599875 156.589384-193.171558 234.060827-288.742217-66.042135 27.891234-130.48689 57.473993-194.833407 87.269599-3.046383 1.410116-5.689582 4.361331-7.734148 7.169283-63.380516 87.044472-126.666888 174.156482-189.926654 261.287935-1.372254 1.890047-2.26253 4.129041-4.349051 8.012488 81.116459 0 160.327522 0 241.365186 0-51.78748 88.22946-102.526071 174.673252-153.265686 261.117043 0.648776 0.457418 1.297552 0.915859 1.946328 1.373277 102.984513-111.606891 205.970048-223.213781 308.954561-334.820672C707.428971 655.450133 706.996113 654.561903 706.563254 653.67265z" p-id="22810"></path>
</svg>

其中 path 为 icon 的绘制路径,调整 icon 初始路径的样式:

填充色 fill="#fff" 、描边及描边的线条粗细 stroke="#1875F0"、stroke-width="10"

并为 path 添加 class 属性,此后可通过 css 样式控制 svg 的样式及交互,实现 icon 的动画展示。

<svg t="1683276478956" class="icon" viewBox="0 0 1034 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22808" width="128" height="128">
  <path class="icon-path" stroke="#1875F0" fill="#fff" stroke-width="10"  d="M796.409538 211.994252c-7.946996-1.175779-11.320836-4.843308-13.861706-12.178367-16.742313-48.327682-45.712111-88.251973-86.432535-118.928646-121.323183-91.397616-289.99372-61.353346-372.841613 66.125023-3.641947 5.604648-7.379061 6.818289-13.512758 6.77224-78.121242-0.590448-147.71732 50.928926-170.386622 125.362172-1.331321 4.371564-5.143137 9.231245-9.136078 11.365862C41.345664 338.041016-5.786797 431.595762 9.520841 530.927071c17.334807 112.485911 115.462708 195.990766 229.862201 195.180308 4.939499-0.034792 11.546987-2.408863 14.496156-6.03546 16.399505-20.168342 31.907711-41.061185 49.092092-63.495126-5.964852 0-9.741875 0.078795-13.513782-0.01228-25.487487-0.620124-51.221592 0.76134-76.41846-2.288113C123.225509 643.405819 58.980299 551.182394 79.021751 462.819904c14.056134-61.975517 52.141544-102.616122 112.443955-122.316813 6.583952-2.15099 10.252505-4.199649 10.352789-12.432147 0.909719-74.740238 76.205613-124.812657 145.456836-96.806813 8.319479 3.364631 12.099572 2.426259 15.017018-6.495948 8.136308-24.88476 21.825075-46.6249 39.251979-66.157769 70.907956-79.476099 199.910029-81.456197 273.16238-4.130064 30.506804 32.203446 48.833195 69.966515 52.519144 114.435309 0.636496 7.689123 2.054799 12.428054 12.196787 11.602246 15.517415-1.264806 31.42778-1.870604 46.813189 0.071631 106.217137 13.408381 180.512237 117.089765 159.665443 221.912136-16.767895 84.314291-82.44471 143.930063-169.095209 153.84897-4.106528 0.469698-8.876158 2.606361-11.707646 5.539156-17.691941 18.331507-34.964326 37.068244-52.240805 55.796794-1.980097 2.146896-3.121084 5.066389-5.27719 8.674567 18.21485 0 34.580586 0.274246 50.934043-0.050142 56.078203-1.113357 107.891266-15.993253 153.187915-49.844222 64.441685-48.15679 100.274798-112.348788 105.933681-192.933127 3.987824-56.780191-9.784854-109.213377-40.996716-156.48603C933.808862 262.176165 873.601618 223.413326 796.409538 211.994252z" p-id="22809"></path>
  <path class="icon-path-yellow" stroke="#f7f258" fill="#fff" stroke-width="10"  d="M706.563254 653.67265c-77.533864 0-155.067728 0-234.922451 0 79.116919-97.599875 156.589384-193.171558 234.060827-288.742217-66.042135 27.891234-130.48689 57.473993-194.833407 87.269599-3.046383 1.410116-5.689582 4.361331-7.734148 7.169283-63.380516 87.044472-126.666888 174.156482-189.926654 261.287935-1.372254 1.890047-2.26253 4.129041-4.349051 8.012488 81.116459 0 160.327522 0 241.365186 0-51.78748 88.22946-102.526071 174.673252-153.265686 261.117043 0.648776 0.457418 1.297552 0.915859 1.946328 1.373277 102.984513-111.606891 205.970048-223.213781 308.954561-334.820672C707.428971 655.450133 706.996113 654.561903 706.563254 653.67265z" p-id="22810"></path>
</svg>

通过 css 样式为 icon 添加动画效果

dasharray & dashoffset 动态描边

这时我们会用到路径描边 stroke 的两个属性

  • stroke-dasharray: 用于控制图形描边路径的虚实。
  • stroke-dashoffset: 用于控制空白间隙相对于绘制起点的偏移量

stroke-dasharray

  • 当 stroke-dasharray 只给定一个参数时:表示虚线长度和每段虚线之间的间距。
.icon-path {
  stroke-dasharray: 100;
}

stroke-dasharray:100 时 虚线长度为100,虚线之间的间距也为100。

image.png

  • 同样的 stroke-dasharray 支持数组参数

    • 当传入两个参数时:一个表示长度,一个表示间距。

    • 当传入三个参数时:表示 虚线长度,间距,虚线长度,如此循环往复绘制图形的描边。

.icon-path {
  stroke-dasharray: 200 50;
}

stroke-dashoffset

stroke-dashoffset 相对于起始点的偏移:

  • 偏移x值为正数时,表示向左移动 x
  • 偏移x值为负数时,表示向右移动 x

需要注意的是,不管偏移的方向是哪一边,dasharray 是 虚线-间隔-虚线-间隔 循环的。

dashoffset 属性需要搭配 dasharray 属性才能看得出效果,因为不是虚线的话是无法看出偏移的。

这里可以参考一下 MDN 的例子,其中红色表示偏移量

image.png

  1. 没有虚线
  2. stroke-dasharray="3 1" ,stroke-dashoffset="0" 设置虚线后没有设置偏移。
  3. stroke-dashoffset="3",向左偏移了3个单位
  4. stroke-dashoffset="-3",向右偏移了3个单位,由于dasharray是循环的,前面偏移的位置会有dasharray填充上
  5. stroke-dashoffset="1",偏移正数,虚线整体左移了1个单位,最终呈现出来的效果跟线段4 一样

dasharray & dashoffset 的使用

dashoffset 与 dasharray 搭配食用可以用于实现动态描边、进度条加载的效果

.icon-path {
 /* 1. animation 指定了名为  icon-path-animation 的动关键帧画效果,
 2. 并在8秒内实现由快到慢的变化效果 
 3. 动画循环播放的效果无限循环播放 
 */
 animation: icon-animation 8s ease-in infinite;
}
@keyframes icon-animation {
   /*  绘制描边路径  */
 0% {
   stroke-dasharray: 4392;
   stroke-dashoffset: 4392;
 }

 40% {
   stroke-dasharray: 4392;
   stroke-dashoffset: 0;
 }
   /*  颜色填充  */
 60% {
   stroke-dasharray: 4392;
   stroke-dashoffset: 0;
   fill: #1875F0;
 }

 100% {
   stroke-dasharray: 4392;
   stroke-dashoffset: 0;
   fill: #1875F0;
 }
}

CSS动画实现的效果:

  1. 从 0 到 40% 绘制描边路径
  2. 从 40% 到 60% 为图形填充颜色
  3. 从 60% 到 100% 的过程中保持不变

整个过程从慢到快,持续 8s, 无限循环

其中 stroke-dashoffset: 4392 到 0 实现了描边路径从无到有的过程

@keyframes icon-animation {
  0% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 4392;
  }

  40% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 0;
  }
  
}

1.gif

stroke-dashoffset 空白间隙偏移为4392 时,那就是 4392 - 4392 = 0,此时的 path 为 0

当 stroke-dashoffset 空白间隙偏移为 0, 4392 - 0 = 4392,也就是这个图标足够显示的路径长度

stroke-dashoffset 的路径长度可以通过以下方式获取

getStroke() {
  const iconPath = document.getElementsByClassName('icon-path') // 获取 path 标签
  for (let i = 0; i < iconPath.length; i++) {
    const item = iconPath[i]
    console.log(item.getTotalLength()) // 获得 path 路径长度
  }
}

找到 path 的最大路径长度作为 stroke-dashoffset 绘制的最大间隙

icon 绘制的完整代码:

<template>
  <div>
    <svg t="1683276478956" class="icon" viewBox="0 0 1034 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22808" width="128" height="128">
      <path class="icon-path" stroke="#1875F0" fill="#fff" stroke-width="10"  d="M796.409538 211.994252c-7.946996-1.175779-11.320836-4.843308-13.861706-12.178367-16.742313-48.327682-45.712111-88.251973-86.432535-118.928646-121.323183-91.397616-289.99372-61.353346-372.841613 66.125023-3.641947 5.604648-7.379061 6.818289-13.512758 6.77224-78.121242-0.590448-147.71732 50.928926-170.386622 125.362172-1.331321 4.371564-5.143137 9.231245-9.136078 11.365862C41.345664 338.041016-5.786797 431.595762 9.520841 530.927071c17.334807 112.485911 115.462708 195.990766 229.862201 195.180308 4.939499-0.034792 11.546987-2.408863 14.496156-6.03546 16.399505-20.168342 31.907711-41.061185 49.092092-63.495126-5.964852 0-9.741875 0.078795-13.513782-0.01228-25.487487-0.620124-51.221592 0.76134-76.41846-2.288113C123.225509 643.405819 58.980299 551.182394 79.021751 462.819904c14.056134-61.975517 52.141544-102.616122 112.443955-122.316813 6.583952-2.15099 10.252505-4.199649 10.352789-12.432147 0.909719-74.740238 76.205613-124.812657 145.456836-96.806813 8.319479 3.364631 12.099572 2.426259 15.017018-6.495948 8.136308-24.88476 21.825075-46.6249 39.251979-66.157769 70.907956-79.476099 199.910029-81.456197 273.16238-4.130064 30.506804 32.203446 48.833195 69.966515 52.519144 114.435309 0.636496 7.689123 2.054799 12.428054 12.196787 11.602246 15.517415-1.264806 31.42778-1.870604 46.813189 0.071631 106.217137 13.408381 180.512237 117.089765 159.665443 221.912136-16.767895 84.314291-82.44471 143.930063-169.095209 153.84897-4.106528 0.469698-8.876158 2.606361-11.707646 5.539156-17.691941 18.331507-34.964326 37.068244-52.240805 55.796794-1.980097 2.146896-3.121084 5.066389-5.27719 8.674567 18.21485 0 34.580586 0.274246 50.934043-0.050142 56.078203-1.113357 107.891266-15.993253 153.187915-49.844222 64.441685-48.15679 100.274798-112.348788 105.933681-192.933127 3.987824-56.780191-9.784854-109.213377-40.996716-156.48603C933.808862 262.176165 873.601618 223.413326 796.409538 211.994252z" p-id="22809"></path>
      <path class="icon-path-yellow" stroke="#f7f258" fill="#fff" stroke-width="10"  d="M706.563254 653.67265c-77.533864 0-155.067728 0-234.922451 0 79.116919-97.599875 156.589384-193.171558 234.060827-288.742217-66.042135 27.891234-130.48689 57.473993-194.833407 87.269599-3.046383 1.410116-5.689582 4.361331-7.734148 7.169283-63.380516 87.044472-126.666888 174.156482-189.926654 261.287935-1.372254 1.890047-2.26253 4.129041-4.349051 8.012488 81.116459 0 160.327522 0 241.365186 0-51.78748 88.22946-102.526071 174.673252-153.265686 261.117043 0.648776 0.457418 1.297552 0.915859 1.946328 1.373277 102.984513-111.606891 205.970048-223.213781 308.954561-334.820672C707.428971 655.450133 706.996113 654.561903 706.563254 653.67265z" p-id="22810"></path>
    </svg>
  </div>
</template>

<script>
export default {
  mounted(){
    this.getStroke()
  },
  methods: {
    // 计算path 最长的路径作为 stroke-dasharray、stroke-dashoffset 的绘制长度
    getStroke() {
      const iconPath = document.getElementsByClassName('icon-path') // 获取 path 标签
      for (let i = 0; i < iconPath.length; i++) {
        const item = iconPath[i]
        console.log(item.getTotalLength()) // 获得 path 路径长度
      }
    }
  },
}
</script>

<style lang='scss' scoped>
.icon-path {
		/* 1. animation 指定了名为  icon-path-animation 的动关键帧画效果,
  		 2. 并在8秒内实现由快到慢的变化效果 
			 3. 动画循环播放的效果无限循环播放 
    */
   animation: icon-path-animation 8s ease-in infinite;
}
@keyframes icon-path-animation {
  0% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 4392;
  }

  40% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 0;
  }

  60% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 0;
    fill: #1875F0;
  }

  100% {
    stroke-dasharray: 4392;
    stroke-dashoffset: 0;
    fill: #1875F0;
  }
}
  
.icon-path-yellow {
  animation: icon-path-animation-yellow 8s ease-in infinite;
}

@keyframes icon-path-animation-yellow {
  0% {
    stroke-dasharray: 2167;
    stroke-dashoffset: 2167;
  }

  40% {
    stroke-dasharray: 2167;
    stroke-dashoffset: 0;
    
  }

  60% {
    stroke-dasharray: 2167;
    stroke-dashoffset: 0;
    fill: #f7f258;
  }

  100% {
    stroke-dasharray: 2167;
    stroke-dashoffset: 0;
    fill: #f7f258;
  }
}
</style>

svg 让 icon 律动起来

通过 svg 的 react 标签绘制最简单的播放 icon ,每一个长方形都是一个 react,再利用 animate 让播放 icon 的每个方块律动起来。

icon 所要实现的效果如下图所示:

<svg width="30px" height="30px" viewBox="0 0 30 30" >
  <rect x="0" y="18" width="2" height="15" fill="#258aff">
    <animate attributeName="height" attributeType="XML" values="38;20;38" begin=".4s" dur=".6s" repeatCount="indefinite"></animate>
    <animate attributeName="y" attributeType="XML" values="28;20;28" begin=".4s" dur=".6s" repeatCount="indefinite"></animate>
  </rect>
  <rect x="5" y="23" width="2" height="10" fill="#258aff">
    <animate attributeName="height" attributeType="XML" values="38;25;38" begin="0s" dur=".7s" repeatCount="indefinite"></animate>
    <animate attributeName="y" attributeType="XML" values="32; 15; 32" begin="0s" dur=".7s" repeatCount="indefinite"></animate>
  </rect>
  <rect x="10" y="18" width="2" height="25" fill="#258aff">
    <animate attributeName="height" attributeType="XML" values="38;28;38" begin=".4s" dur=".6s" repeatCount="indefinite"></animate>
    <animate attributeName="y" attributeType="XML" values="26; 13; 26" begin=".4s" dur=".6s" repeatCount="indefinite"></animate>
  </rect>
  <rect x="15" y="18" width="2" height="15" fill="#258aff">
    <animate attributeName="height" attributeType="XML" values="38;23;38" begin=".2s" dur=".6s" repeatCount="indefinite"></animate>
    <animate attributeName="y" attributeType="XML" values="28; 13; 28" begin=".2s" dur=".6s" repeatCount="indefinite"></animate>
  </rect>
</svg>

animate 中的 values 用来设置目标属性在各个关键帧值的大小。它的值是一个由分号组成的列表,每项的类型取决于 attributeName 操作属性的值类型。

  • animate attributeName="y" 设置不同的 react 在 y 轴的起始位置变化,以 svg 的左上角为原点(0,0)。在位置发生变化时 react 的高度可能发生短缺的情况,所以需要结合小方块的高度一起律动。
  • animate attributeName="height" 设置不同的 react 的各个时期分高度变化。

在制作的过程中分析动画所涉及的属性,耐心调整动画的速度以达到自己想要的变化效果。律动的 icon 就是那么简单的实现啦,大家可以一起来试一试哦!

总结

本文介绍了 svg 图像的相关使用,并通过几个简单的小案例实现了 svg 最基本的使用和动画。

当然 svg 还能呈现出更加丰富多彩的动画效果,例如 炫酷的 霓虹灯字体

如果大家感兴趣的话可以继续研究哦!

😊 相信在你的打磨和实践之下一定可以做出自己满意的动画 !!!

参考

  1. 快速实现SVG动态图标
  2. png、jpg、gif三种图片格式的区别
  3. SVG是什么格式?终于有人讲明白了!
  4. stroke学习之stroke-dasharray和stroke-dashoffset