SVG初步实践

一、SVG是什么

SVG 意为可缩放矢量图形(Scalable Vector Graphics),是基于 XML 的矢量图形描述语言,可以近似理解成 HTML,所以能和 JS 以及 CSS 进行交互。

不要把 SVG 和 CSS,Canvas,HTML 搞混,他们之间并没有你中有我,我中有你的关系。

SVG 就是一个 XML,例如:

<svg x="0px" y="0px" width="450px" height="100px" viewBox="0 0 450 100">
     <rect x="10" y="5" fill="white" stroke="black" width="90" height="90"/>
     <circle fill="white" stroke="black" cx="170" cy="50" r="45"/>
     <polygon fill="white" stroke="black" points="279,5 294,35 328,40 303,62 309,94 279,79 258,94 254,62 230,39 263,35"/>
     <line fill="none" stroke="black" x1="410" y1="95" x2="440" y2="6"/>
     <line fill="none" stroke="black" x1="360" y1="6" x2="360" y2="95"/>
</svg>

二、SVG绘制路径

SVG提供了 rect、circle、ellipse、line、polyline、polygon 六种基本形状用于图形绘制,这些形状可以直接用来绘制一些基本的形状,如矩形、椭圆等。

而复杂图形的绘制需要使用 path:

<path>通过属性 d 来定义路径,具体值是由专门的“指令字母+坐标值”实现,每一个命令对应一个字母,并且区分大小写

  • 大写:  参照的是绝对坐标,即 SVG 的右上角
  • 小写:  参照的相对坐标,即 前一个点 的坐标

<path>属性 d 主要有以下几个命令:

其中,有5个指定属于基本指令,你也可以理解为“好理解好上手好记忆”的指令,见下表:

 

除了这5个参数少、直来直往的指令,剩下的,除了弧形命令A(Arcs),就都是与贝塞尔曲线相关的命令了。数学比较差,暂不介绍。

三、SVG从哪里来

虽然利用SVG的标签可以画出任意的图形,但是对于复杂图形而言,靠手写坐标既费力又费时,这时候就需要借助第三方工具,例如 Adobe Illustrator CC

先在 AI 工具中画好图像,然后导出成 svg 文件即可(这块的工作可以交给 UI 的同事)

四、动画方式

svg 文件拿到了,怎样才能让它动起来呢,这里就要讲一下动画相关的姿势了~~~ ?

动画变换其实就是图画从 A 状态 过渡到 B 状态的过程,可以用SMIL、Javascript、CSS3 方式实现。

SMIL

SMIL 全称 Synchronized Multimedia Integration Language,该语言被 SVG 原生支持,主要使用标签来描述动画。

据传,由于性能的问题以及 CSS Animation 越来越强大,Chrome 会在未来的版本中废弃对 SMIL 的支持。

不过 MDN 上的声明却说 Chrome 推迟了废弃时间,也不知道是什么情况。

总而言之,被 Chrome 这么一搞还是不推荐大家入门 SMIL 了。

Although Chrome 45 deprecated SMIL in favor of CSS animations and Web animations, the Chrome developers have since suspended that deprecation.
via: SVG animation with SMIL

JavaScript

使用 JS 来操作 SVG 动画自不必多说,网上已经有很多现成的类库,例如老牌的 Snap.svg 以及 anime.js,都能让我们快速制作 SVG 动画。

当然,除了这些类库,HTML 本身也有原生的 Web Animation 实现,使用 Web Animation 也能让我们方便快捷地制作动画。

CSS3

这里主要是使用 CSS3 动画三剑客 ( animation, transform, transition ) 来实现动画。

css 优点不言自明,它比 JS 更加简单方便,关键帧的配合可以让其实现很多复杂的动画。

 

下文主要就从 CSS3 的角度来描述 SVG 动画的方法。

 

五、CSS3 动画方法

动画可以拆成两部分,起始状态和终止状态是一部分,两态中间的过渡是另一部分。

而两态的变换,简单的来说可以分为 变换 和 变形 两种,即线性变换和非线性变换。

变换动画
平移 translate

平移变换主要是利用 translate 等方法让元素移动位置达到的动画,只要善于使用,简单的移动也能做出不错的动效,例如:

这里有一个小技巧,就是在元素移动的起点和终点分别设置标记元素。使用 getClientRects() 方法获取标记元素的绝对位置,通过计算标记元素的位置差,换算成平移的距离。这样利用纯 CSS 就实现了位置移动的有趣动画。

旋转 rotate

旋转变换是使用 rotate 方法让元素进行旋转。使用时请注意:旋转圆心默认在 SVG 的左上角。可以通过设置 transform-origin 来修改圆心。

.rotate {

  transform: rotate(45deg);

  transform-origin: 100px 100px

}

由于旋转圆心如此重要,所以正如下面的这个例子,我们首要确认的是旋转圆心,剩下的就是简单的旋转角度的控制问题了。

缩放 scale

缩放变换则是使用 scale 方法让元素进行变大变小, 它的本质实际上是元素点 (x, y) 被乘以缩放系数了,即 (x * sacle, y * scale)。所以当设置了 transform: scale(2) 时,它相当于元素所有的坐标点变成 (2x, 2y) 了。

讲的这么详细主要是想让大家注意一个问题,当放大系数为负数的时候,例如 -1 那元素坐标点就会变成 (-x, -y),这就相当于元素针对横纵坐标分别做了一次镜像。对于一些需要实现镜像效果的元素可以使用这招。

斜歪 skew

斜歪变换是使用 skew 方法使元素进行倾斜变形的方法。

矩阵变换 matrix

矩阵变换是所有变换方法的底层实现,上述变换方法都是矩阵变换某个方向的特例,完全可以使用矩阵变换实现。矩阵变换的使用方法就不多介绍了,不清楚的可以查下文档。一个 2D 的矩阵变换如下所示:

matrix

关于为何使用矩阵来进行矩阵变换计算,感兴趣的小伙伴可以参考《从矩阵与空间操作的关系理解CSS3的transform》,这篇文章浅显易懂的解释了使用矩阵表示的精妙之处。

变形动画

变形动画即非线性变换动画,这里主要讲述描边,路径轨迹和路径变形三种变形动画。

描边动画

描边动画算是经典的 SVG 动画了,给人一种绘画的视觉效果,所以描边动画的名字因此得来。

它主要是利用 Path 的描边长度 stroke-dasharray 配合描边起始位置偏移量 stroke-dashoffset 来实现的动画。

首先设置 stroke-dasharray 等于路径长度,然后将 stroke-dashoffset 从路径长度减少到 0 ,就完成了一次描边。

可以看到,描边动画其实主要在于设置描边的长度。

除了在 AI/Sketch 等软件中获取路径长度外,我们还可以使用 JS 的 getTotalLength() 方法获取。

除了使用 CSS3 Animation 来实现动画外,我们也可以使用 JS 来绘制动画,网上有很多现成的类库。

其中比较推荐大家使用 drawsvg 这款描边库,能简单快速地实现很多复杂的描边特效。

路径轨迹动画

路径轨迹动画简单来说就是让元素沿着某条路径进行运动的动画。如果用其它方法实现这个效果的话可能会略复杂,但是用 SVG 就非常得心应手了,这里主要说一下 CSS3 方法。

首先设置 offset-path 属性为某一条 Path 路径,然后通过改变 offset-distance 的距离值,就可以实现元素沿着 offset-path 运动的动画。

路径变形动画

严格意义上,路径变形动画才真正算的上“变形”动画,它表示的是路径之间不规则变化的动画。

从 A 路径变形成 B 路径,我们现在可以直接使用 CSS3 Animation 来实现,浏览器会自动帮我们做补间动画。当然我们也可以使用一些 JS 库例如 Snap.SVG 来制作。

不管是使用 CSS3 还是使用 Snap.SVG,都需要注意一个问题,那就是两条变形路径内的描点数需要保证一致,而且需要都是简单的描点,不能存在弧线等高级描点。这么实现的原因很好理解,能够极大减少补间动画的运算量,本质上可以归类为每个描点的平移动画。

但是这种限制对于开发者来说还是颇为麻烦的,所以很多类库就想要解决这个问题。其中比较著名的有 GreenSock,它们编写的 GSAP 插件据说能够对路径进行任意变形,从官网首页的动画也可窥见一二。另外一款插件是 SVG Morpheus,也能实现同样的效果,不过可能因为算法的问题,补间动画似乎并没有 GSAP 那么流畅。另外最近腾讯 AlloyTeam 发布了一款插件 Pasition ,用来实现路径过渡效果,使用起来也很方便,感兴趣的同学可以试一试!

六、实践

实践检验真理,本例子中使用Javascript方式实现,引用腾讯公司的开源插件 Pasition

先上效果图:

 

代码如下:

  1 <html>
  2 
  3     <head>
  4         <title>svg demo use pasition.js</title>
  5     </head>
  6     <body>
  7         <div class="mainContener">
  8             <svg id="line" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 798.8 876.8">
  9                 <defs>
 10                     <path id="cls1" d="M790.13,238.63A398.06,398.06,0,0,1,392.06,636.7c-61.66,0-129.31-30.75-181.41-55.78C77,516.72-6,396.81-6,238.63-6,128.71,48.19,40,120.23-32S285.08-172.94,395-172.94,570.73-101.71,642.76-29.68,790.13,128.71,790.13,238.63Z"/>
 11                     <path id="cls2" d="M790.13,238.63c0,115.28-34.42,250.22-112.73,322.92-71,65.94-162,56.36-266.55,56.36-61.66,0-133.42-.8-185.53-25.83-38-18.26-80.24-75.71-110.37-104.51C39.09,415.09-4.83,351.84-4.83,238.63c0-109.92,63-178.07,135-250.11S284.49-206.4,394.41-206.4,567.46-75.54,639.5-3.5,790.13,128.71,790.13,238.63Z"/>
 12                     <path id="cls3" d="M790.5,238.5c0,117.28-46,191.9-126,265.51-71.73,66-157.87,164.39-263,164.39-48.38,0-101.22-34.95-144.19-50.59A403.66,403.66,0,0,1,77.44,487C24.05,418.65-1.3,320.32-1.3,226.89-1.3,99.71,51.19-1.37,143.25-75.18c69-55.29,157.08-51.37,252.34-51.37,101.69,0,203,10.88,273.91,73.05C753.66,20.32,790.5,117.76,790.5,238.5Z"/>
 13                     <path id="cls4" d="M790,239c0,128.23-63.58,241.76-158.53,313.58C565.55,602.43,484,609.1,395,609.1c-47.5,0-109.32-2.18-151.48-17.61C165.17,562.82,114.19,526.6,68.23,458.57,25.81,395.79-4.24,320.69-4.24,239.22-4.24,124.67,50,21,128.16-50.93c70.05-64.45,158.27-90.3,261-90.3,96.91,0,189.77,37,258.33,95.11C732.59,26,790,118.66,790,239Z"/>
 14                     <path id="cls5" d="M790.5,238.5c0,70.8-15.7,142.42-48.51,199.75-67.59,118.08-203,191.4-348.75,191.4A390.63,390.63,0,0,1,148,543.55C58.41,471.67-2.48,361.26-2.48,237.46c0-107.11,46.46-204.19,116.06-275A391,391,0,0,1,393.24-154.73C505.44-154.73,612-88,683.5-12.5,750,57.78,790.5,134.1,790.5,238.5Z"/> 
 15                 </defs>
 16                 <path class="cls-1" transform="translate(7.17 207.9)"/>
 17                 <path class="cls-2" transform="translate(7.17 207.9)"/>
 18                 <path class="cls-3" transform="translate(7.17 207.9)"/>
 19                 <path class="cls-4" transform="translate(7.17 207.9)"/>
 20                 <path class="cls-5" transform="translate(7.17 207.9)"/>
 21             </svg>
 22         </div>
 23     
 24     </body>
 25     <style>
 26             body{background-color: black}
 27             .mainContener{width: 500px;height: 500px;margin: 100px auto}
 28             .mainContener svg{
 29                     margin: 20px auto;
 30             }
 31             .cls-1,.cls-2,.cls-3,.cls-4,.cls-5{fill:none;stroke-miterlimit:10;}
 32             .cls-1{stroke:#f455a4;}.cls-1,.cls-4{stroke-width:2.35px;}.cls-2{stroke:#8faffc;stroke-width:3px;}.cls-3{stroke:#f64629;}.cls-4{stroke:#997aff;}.cls-5{stroke:#ffd500;}
 33             
 34     </style>
 35 
 36     <script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
 37     <script src="https://unpkg.com/pasition@1.0.1/dist/pasition.js"></script>
 38     <script type="text/javascript">
 39 
 40         $(() => {
 41             var path1 = document.querySelector('#cls1').getAttribute('d');
 42             var path2 = document.querySelector('#cls2').getAttribute('d');
 43             var path3 = document.querySelector('#cls3').getAttribute('d');
 44             var path4 = document.querySelector('#cls4').getAttribute('d');
 45             var path5 = document.querySelector('#cls5').getAttribute('d');
 46 
 47             animate(".cls-1", [path1, path2, path3, path4, path5], 0, 1);
 48             animate(".cls-2", [path1, path2, path3, path4, path5], 1, 2);
 49             animate(".cls-3", [path1, path2, path3, path4, path5], 2, 3);
 50             animate(".cls-4", [path1, path2, path3, path4, path5], 3, 4);
 51             animate(".cls-5", [path1, path2, path3, path4, path5], 4, 0);
 52                 
 53         });
 54         
 55         pasition.toSVGString = function (shapes) {
 56             /*克隆一下实时数据*/
 57             //var shapes = JSON.parse(JSON.stringify(shapes||[]));
 58             /*对数据中的每个点数组做处理
 59             * */
 60             return shapes.map(function(shape){
 61                 shape.forEach(function (point, idx) {
 62                     if (!idx) {
 63                         /*
 64                             * 若是第一个点数组,那么对该点数组的处理是前面加M,然后前两个点后面加C
 65                             * */
 66                         point.splice(2, 0, "C");
 67                         point.unshift("M");
 68                     } else {
 69                         /*
 70                             * 除了第一个点数据外,所有的点数组的前两个点删除掉
 71                             * */
 72                         point.splice(0, 2, "C");
 73                     }
 74                 });
 75                 return shape.map(function (point) {
 76                     return point.join(" ");
 77                 }).join("");
 78             }).join("")
 79         
 80         };
 81 
 82         function animate(classname, arr, index1, index2) {
 83             var dom = document.querySelector(classname);
 84             pasition.animate({
 85                 from: arr[index1],
 86                 to: arr[index2],
 87                 time: 5000,   //5s完成动画过渡
 88                 progress: function (shapes) {
 89                     dom.setAttribute("d", pasition.toSVGString(shapes));
 90                 },
 91                 end: function () {
 92                     index1++
 93                     index2++
 94                     if (index1 === arr.length) index1 = 0
 95                     if (index2 === arr.length) index2 = 0
 96                     animate(classname, arr, index1, index2)
 97                 }
 98             })
 99         }
100 
101     </script>
102 </html>

七、后记

本文只是涉猎了一些常用的变换方法以及一些基本的变形动画,SVG 还有很多例如遮罩、滤镜、填充动画方法,感兴趣的同学可以去了解下,都能实现非常有趣的动画特效。不过万变不离其宗,只要你富有想象力,能够设计出一些别出心裁的动效,即使只使用上文提及的一些简单的动画方法,我相信最终的动效也一定是非常有意思的!

 

参考资料:

SVG动画实践

SVG基本形状path路径置换

深度掌握SVG路径path的贝塞尔曲线指令

 

转载于:https://www.cnblogs.com/waxdl/p/9103580.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值