踏足SVG | 深入浅出全方位介绍SVG的功能及特性 - 基础篇(三)

前言

上一篇文章中介绍了 SVG 中常用的元素的使用方法。这一篇文章,主要对 <path> 进行详细的介绍,包括 path 路径的参数以及 path 常见的一些动画效果进行说明。

往期文章

踏足SVG | 深入浅出全方位介绍SVG的功能及特性 - 基础篇(一)

踏足SVG | 深入浅出全方位介绍SVG的功能及特性 - 基础篇(二)

path

path 可以实现矩形、圆、线等等上一篇文中中提到的所有图形,通过设置路径还可以事项更复杂的图形,就如同你拿着笔自己画画一样,它有一套属于自己的语法规则用来定义路径。

下面将介绍一些 path 的基本用法,如果已经了解过的可以直接看 path 动画部分

基本属性:

  • d:一个点集数列以及其它关于如何绘制路径的信息。

序列指令 d

属性 d 的值是一个“命令+参数”的序列

每一个命令都用一个关键字母来表示,比如,字母“M”表示的是“Move to”命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的x和y轴坐标。比如移动到(10,10)这个点的命令,应该写成“M 10 10”。这一段字符结束后,解析器就会去读下一段命令。每一个命令都有两种表示方式,一种是用大写字母,表示采用绝对定位。另一种是用小写字母,表示采用相对定位(例如:从上一个点开始,向上移动10px,向左移动7px)。

Move To

移动画笔,只是移动画笔,不会连线

命令:

  • M x y (目标点在画布的绝对坐标)
  • m dx dy (目标点相较于上一个点的相对坐标)

Line To

目标点连线,会在当前点和目标点直接连一条线

命令:

  • L x y (目标点在画布的绝对坐标)
  • l dx dy (目标点相较于上一个点的相对坐标)

Horizontal

绘制一条水平线

命令:

  • H x (水平移动到的画布的 x 的位置值(绝对位置))
  • h dx (由当前点水平移动 dx 距离(相对位置))

Vertical

绘制一条垂直线

命令:

  • V y (垂直移动到的画布的 y 的位置值(绝对位置))
  • v dy (由当前点水平移动 dy 距离(相对位置))

Close Path

闭合路径,连接最后一个点和起始点。实测中 path 都是自动闭合路径

命令:

  • Z 或 z (两个一样)

Cubic Béziers

三次贝塞尔曲线需要给定一个目标点和两个控制点

Cubic_Bezier_Curves.png

命令:

  • C x1 y1, x2 y2, x y(绝对坐标)
  • c dx1 dy1, dx2 dy2, dx dy(相对坐标)

S命令可以用来创建与前面一样的贝塞尔曲线,但是,如果S命令跟在一个C或S命令后面,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。如果S命令单独使用,前面没有C或S命令,那当前点将作为第一个控制点。

ShortCut_Cubic_Bezier.png

命令:

  • S x2 y2, x y (绝对坐标)
  • s dx2 dy2, dx dy (相对坐标)

Quadratic Béziers

二次贝塞尔曲线Q,它比三次贝塞尔曲线简单,只需要一个控制点,用来确定起点和终点的曲线斜率

命令:

  • Q x1 y1, x y (绝对坐标)
  • q dx1 dy1, dx dy(相对坐标)

T 命令和三次贝塞尔曲线的 S 差不多,用于延长二次贝塞尔曲线,T 只需要一个目标点

命令:

  • T x y (绝对坐标)
  • t dx dy (相对坐标)

Arcs

弧形命令A是另一个创建SVG曲线的命令。基本上,弧形可以视为圆形或椭圆形的一部分。假设,已知椭圆形的长轴半径和短轴半径,并且已知两个点(在椭圆上),根据半径和两点,可以画出两个椭圆,在每个椭圆上根据两点都可以画出两种弧形。

命令:

  • A rx ry x-axis-rotation large-arc-flag sweep-flag x y (绝对坐标)
  • a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy(相对坐标)

例子

 <!-- L、l 命令绘制 w:100 h:40 的矩形 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 10 L 110 10 L 110 50 L 10 50" fill="orange"/>
  </svg>
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 10 l 100 0 l 0 40 l -100 0" fill="orange"/>
  </svg>

  <br/>
  <!-- H/V、h/v 命令绘制 w:100 h:40 的矩形 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 10 H 110 V 50 H 10" fill="orange"/>
  </svg>
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 10 h 100 v 40 h -100" fill="orange"/>
  </svg>

  <br/>
  <!-- Z/z 命令自闭和路径 -->
  <!-- path 好像是默认自闭合的 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 10 H 110 V 50 H 10 z" fill="orange"/>
  </svg>
复制代码
 <div>C</div>
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 C 40 50 70 150 110 100" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 c 30 -50 60 50 100 0" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>
  <div>S</div>
  <br/>
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 C 40 50 70 150 110 100 S 150 150 190 100" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <div>Q</div>
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 Q 60 180 110 100" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 q 50 80 100 0" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <div>T</div>
  <!-- 快乐的画波浪 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M 10 100 Q 30 180 50 100 T 90 100 T 130 100 T 170 100 T 210 100" stroke="orange" fill="transparent" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <div>A</div>

  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <!-- 起始点 10 100 -->
    <!-- 移动 1/4 个圆到点 50 140 这里点的确定未 x = sx + rx y = xy + ry -->
    <!-- 移动到圆形的中心点完成连线 -->
    <path d="M10 100 A 40 40 0 0 0 50 140 L 50 100 Z" stroke="orange" fill="green" stroke-width="3" stroke-linecap="round"/>
  </svg>
  <br>
  <!-- 小弧 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M80 100 A 40 40 0 0 0 120 140 " stroke="orange" fill="green" stroke-width="3" stroke-linecap="round"/>
  </svg>
  <!-- 大弧 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M80 100 A 40 40 0 1 0 120 140 L 120 100 Z" stroke="orange" fill="green" stroke-width="3" stroke-linecap="round"/>
  </svg>

  <br/>
  <!-- 从起点到终点画弧形 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M80 100 A 40 40 0 1 0 120 140" stroke="orange" fill="green" stroke-width="3" stroke-linecap="round"/>
  </svg>
  <!-- 从终点到起点画弧形 -->
  <svg class="border" width="200" height="200" viewBox="0 0 200 200">
    <path d="M80 100 A 40 40 0 1 1 120 140 L 120 100 Z" stroke="orange" fill="green" stroke-width="3" stroke-linecap="round"/>
  </svg>
  <br />
  <svg class="border" width="325px" height="325px" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M80 80
             A 45 45, 0, 0, 0, 125 125
             L 125 80 Z" fill="green" stroke="orange" stroke-width="3"/>
    <path d="M230 80
             A 45 45, 0, 1, 0, 275 125
             L 275 80 Z" fill="red" stroke="orange" stroke-width="3"/>
    <path d="M80 230
             A 45 45, 0, 0, 1, 125 275
             L 125 230 Z" fill="purple" stroke="orange" stroke-width="3"/>
    <path d="M230 230
             A 45 45, 0, 1, 1, 275 275
             L 275 230 Z" fill="blue" stroke="orange" stroke-width="3"/>
  </svg>

复制代码

常用的 path 动画

下面将介绍 path 常见的三种动画。

描边动画

如下图所示

path1.gif

主要使用 stroke-dasharray、stroke-dashoffset 和 animation 动画来完成

简单来说明一下 stroke-dasharray 和 stroke-dashoffset 这两个属性。

首先我们先想一下,如果我们想用 svg 来实现一条虚线,该如果去实现。写多条 line 吗,还是通过 path 用 M 来移动画笔从而画不同的线呢。

以上说的两种方法都可以实现画一条虚线,但是在 svg 里面提供了这么一条属性,可以让你更加轻松的完成一条虚线

stroke-dasharray

一条虚线是由一段实线和一段空隙来组成的。stroke-dasharray就是来控制这一段实线和一段空隙的大小

stroke-dasharray:

  • none (默认)
  • inherit 继承
  • dasharray 一个数组是控制虚线的绘制纹理的规则。

dasharray 可以这么理解:

奇数位代表实线长度,偶数位代表虚线长度。如果线太长了,超出了数组内定义长度的总和值,那么后续部分将重新按照数组内定义的长度依次补全。即重复循环使用,有点像数组 concat。

一个简单的例子

    <svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <!-- 实线 20 空隙 20,40 个单位之后依然有线,重新以定义的 20,20 绘制。同 20,20,20,20。以此类推 -->
      <path stroke-dasharray="20,20" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
    </svg>
    <svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <!-- 同 20,20 -->
      <path stroke-dasharray="20,20,20,20" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
    </svg>
    <svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <!-- 实线 20 空隙 20 实线 10,50 个单位之后依然有线,重新以定义的 20,20,10 绘制。同 20,20,10,20,20,10。以此类推 --> 
      <path stroke-dasharray="20,20,10" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
    </svg>
    <svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <!-- 实线 20 ,20 个单位之后依然有线,重新以定义的 10 绘制。同 20,20。以此类推 -->
      <path stroke-dasharray="20" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
    </svg>
复制代码

path1.png

stroke-dashoffset

stroke-dashoffset 是来设置描边线的一个初始位移量。正值想逆时针方向移动,负值相反。这个需要设置 stroke-dasharray 才能看的出来。

stroke-dashoffset: 20% | 20 | inherit

如果使用了一个百分比值, 那么这个值就代表了当前viewport的一个百分比。viewport的宽和高都会有影响。

举个例子

 <svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <path stroke-dasharray="400" stroke-dashoffset="20" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
 </svg>
复制代码

path2.png

有了前面的这些知识,其实这个描边动画就变得简单了,只需要做以下几步

  1. 通过 stroke-dasharray 设置一个虚线的纹理,纹理的规则我希望是以 path 的图形总长度来设置实线和间隙
  2. 通过 stroke-dashoffset 设置一个虚线的位移,位移的距离就是 path 的图形总长度。这样一来这个 path 展示的都是虚线的空隙,实线部分就被隐藏起来了
  3. 通过 animation 属性设置动画,将 stroke-dashoffset 的值动态变为 0

PS: path 的图形总长度可以在 path 的 dom 上,通过 getTotalLength 方法获取。

具体代码如下

<svg class="border" width="200" height="200" viewBox="0 0 200 200">
      <path id="myPath" stroke-dasharray="400" stroke-dashoffset="400" d="M 10 10 H 110 V 110 H 10 Z" fill="orange" />
</svg>
复制代码
  path {
    stroke: orange;
    stroke-width: 6;
    /* 让连接更顺畅 */
    stroke-linecap: round;
    stroke-linejoin: round;
    fill: transparent;
    animation: line-anm 2s ease-in-out forwards;
  }
  @keyframes line-anm {
    to {
      stroke-dashoffset: 0;
    }
  }
复制代码

path 轨迹

使用 animateMotion 和 mpath 在定义你想要的运动轨迹。这里会在 SMIL Animation 篇幅中详细说明

<svg class="border" width="200" height="200" viewBox="0 0 200 200">
  <path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110" stroke="lightgrey" stroke-width="2" fill="none" id="theMotionPath" />
  <path fill="red" d="M -5 -5 L 10 0 L -5 5 L 0 0 Z">
    <animateMotion dur="6s" repeatCount="indefinite" rotate="auto">
      <!-- 使用 mpath 引用外部 path 路径-->
      <mpath href="#theMotionPath" />
    </animateMotion>
  </path>
</svg>
复制代码

动态 path 路径

我们都知道,在定义 path 的时候是通过 d 属性来规划 path 的具体路径的。再加上 SVG 标签本身的特性,定义的属性会即时的反馈在 dom 上从而重新进行渲染。

通过给 d 属性添加 transition 过渡效果来实现动画。

可用通过 css 和 js 两种方式修改 d 属性

举个例子:path in css

  <svg id="path1" class="border" width="200" height="200" viewBox="0 0 200 200">
    <path  d="M 50 90 L 100 150 L 150 90" fill="transparent" stroke="orange" stroke-width="5"/>
  </svg>
复制代码
svg path {
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: d ease-in-out 0.2s;
}
#path1:hover path{
  d: path("M 50 90 L 100 30 L 150 90");
}
复制代码

path2.gif

再举个例子:path in js

  <svg id="path2" class="border" width="200" height="200" viewBox="0 0 200 200">
    <path  d="M 50 40 L 100 150 L 150 40" fill="transparent" stroke="orange" stroke-width="5"/>
  </svg>
复制代码
svg path {
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: d ease-in-out 0.2s;
}
复制代码
const path2 = document.querySelector('#path2 path')
setTimeout(()=>{
  path2.setAttribute('d', "M 50 60 L 100 120 L 50 180")
},1000)
复制代码

path3.gif

写在最后

参考资料

参考来源 - MDN

Supongo que te gusta

Origin juejin.im/post/7067071867986640909
Recomendado
Clasificación