frontend9/fe9-library

一种基于svg实现的关键帧动画思路

t517134500 opened this issue · 1 comments

前言

随着时代的发展,市场对web应用的效果要求越来越高。之前的前端效果已经很大程度上不能满足市场对一些特效的需求,尤其是在一些复杂的不规则图形的效果变化上的需求。而目前对于这些效果的实现一般是前端嵌入flash或视屏、或者使用gif。这些实现方式既增加研发成本也不优雅。
本文将提出一种前端解决这类动画效果的一种思路。

预期效果

话不多说,先上预期效果图:
a

分析

源于flash的关键帧概念,我们可以将这个复杂效果按时间分割成关键的几个画面,然后让浏览器来填充缺失的部分,从而形成期望的效果。
而要实现这个效果就有几个技术难点需要解决:
1.如何让浏览器对关键帧进行补充过渡帧。
2.如何快速生成关键帧的描述给浏览器。

生成过渡帧

这里运用到了svg的animate标签的特性:
在svg中基本所有可视标签上的属性都可以使用animate标签添加动画效果。而在path标签上的路径描述属性 d 也是可以通过 animate进行变化的,其中 values确定动画的关键帧,keyTimes关键帧间隔时间,keySplines确定关键帧的过度方式。(这里只讨论用到的属性 );
通过svg的这一技术可以达到自动生成过度帧效果。
如:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <path > <animate attributeName="d" keyTimes="0; .7; 1" keySplines='0 .75 .25 1;0 .75 .25 1' values="M75 0 L75 200 L225 200 Z;M225 0 L75 200 L225 200 Z;M75 0 L75 200 L225 200 Z;" begin="0s" dur="2s" repeatCount="indefinite" /> </path> </svg>
b
这里svg补充关键帧有自身的限制
1.d属性中所有的命令参数和对应的控制点数量必须相同;
即M 0,0 L 10,10 Z对应的其他关键帧中的第一个路径命令必须是M 第二个必须是L 第三个必须是Z,以此类推。
2.基于第一条,所有的关键帧中控制点必须一一对应;

生成关键帧

由于svg自身补充关键帧有一定的限制,所以在生成各个关键帧路径时不能用UI给出的svg图像。
所以这里需要前端基于一个最终效果的svg图像,由后向前推出其他的关键帧描述。
这里涉及到path路径的解析以及path路径可视化;

path路径解析

path路径上的每个英文路径命令都有自己的意义:
M = moveto 2个数字参数
L = lineto 2个数字参数
H = horizontal lineto 1个数字参数
V = vertical lineto 1个数字参数
C = curveto 6个数字参数
S = smooth curveto 4个数字参数
Q = quadratic Belzier curve 4个数字参数
T = smooth quadratic Belzier curveto 2个数字参数
A = elliptical Arc 7个数字参数
Z = closepath 0个数字参数
其中如果是小写字母则是相对路径(相对于之前的坐标偏移参数量),而大写字母则是绝对路径(相对于原点的绝对位置);
根据这一规律,我们可以借鉴mvvm框架模式,将svg的每个path控制点解析成屏幕坐标系上边的一个点,通过修改对应坐标点参数来控制path图形形状。
c
demo:http://120.55.57.47:8080/base/Desktop/game/map_test1.html
补充:
1.有部分path命令处理起来要稍微特殊一些,这些需要注意一下(如H,V,A,Z)。
e

2.svg如果带有viewBox缩放,则需要对屏幕坐标和svg内部坐标进行转换。
示例:
self._svg = document.getElementById('svg'); ......... self.SVGpoints = self._svg.createSVGPoint(); ......... var e = event || window.event; var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; var scrollY = document.documentElement.scrollTop || document.body.scrollTop; self.SVGpoints.x = e.pageX || e.clientX + scrollX; self.SVGpoints.y = e.pageY || e.clientY + scrollY; self.SVGpoints = self.SVGpoints.matrixTransform(self._svg.getScreenCTM().inverse());
附完整版命令解析
demo:http://120.55.57.47:8080/base/Desktop/game/map_test_f.html

批量修改控制点的一种思路

通过上述的方法其实已经可以 实现关键帧的编辑,但是一个复杂的svg图形往往有大量的path控制点组成,
如果再一个一个的去操作控制点会造成大量的重复功能,因此需要一种大范围操作控制点功能。
这里可以运用一些个控制点特性来处理这个问题:
任何path命令控制点重合后将会等于没有绘制
d
任何path命令控制点重合后且与指点的命令控制点不重合将会等于绘制一条直线
f
任何弧线都和用有限的直线近似表示
任何path的dom对象都可以用getPointAtLength来获取相对长度位置的点的位置

这里都没有考虑圆弧命令A,默认将其转化为C命令近似模拟。

通过这些特性,我们可以通过选中两个控制点控制其中间所有的控制点按照一定path路径将所有命令点的控制按照上述规律摆放到位生成新的path路径。

总结

通过上述的方法,我们可以快速的通过可视化操作来生成多个关键帧path路径,然后在根据svg动画的特性生成一些酷炫的动画。
优点:可以快速完成效果;
缺点:如果是复杂的效果生成的文件可能有些大,如果直接嵌入html代码中则不利于页面开发。效果的精细程度跟帧数成正比,对于精细的效果可能还是不过方便。
附完成的效果demo
http://120.55.57.47:8080/base/Desktop/fruit.svg