SVG+JS path等值变化实现CSS3兴叹的图形动画
SVG+JS path等值变化实现CSS3兴叹的图形动画
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=4225
一、CSS3动画交互的局限
CSS3也能实现很多精彩的动画效果,配合transform
新增的多个变换,以及animation
的延时、重复次数、前后端状态控制等,可以实现很多精彩的效果。
然而,也是有局限的。很明显,CSS3对图形所做的变化效果,往往都是基于图形整体的动画。什么意思呢?比方说,一个四边矩形,可以让他捏成足球(border-radius
,圆角矩形)、然后再旋转、拉伸或者缩放,无论这些动画效果如何,我们依然可以窥见其四条边的存在,虽然可能位置、角度或弧线不一样了。
但是,当我们想把四边形变换成五边形的时候,就会捉襟见肘,虚弱乏力。图形本身的基础变化了,这是CSS3无法驾驭的。
换个文艺的概括,CSS3就是「形可得而神不备」。
因此,对于动其筋骨的图形变换,在web上,我们可能需要借助其他*的力量,比方说Canvas绘图或者SVG标记。
二、SVG图形动画
SVG本质上就是HTML,能和CSS卿卿我我,打得火热。因此,CSS2/CSS3的一些特性,SVG也是可以受用的。例如,尺寸变化,位置变化,各种颜色变化,transform
变换(虽然SVG本身有自己的transform
变换属性
– 见下图)等~
同时SVG一些特有的属性,例如fill
, stroke-*
等也能直接在CSS中设置,相当强大和方便。//zxx:
具体应用可参见:“纯CSS实现帅气的SVG路径描边动画效果”一文。
不过上面的动画本质上依然是“CSS3图形动画”。差别仅仅是作用在SVG元素上。
而标题这里的“SVG图形动画”指的是:需要借助JS对SVG特定元素属性值做连续变化实现的图形动画效果。 简单来说,就是“只能使用JS驱动实现的SVG图形动画”。
举个大力神杯:
SVG中的圆使用<circle>
元素以及中心点属性cx
, cy
和半径属性r
表示。现在,我们想水平位移200像素,怎么办?
指望CSS3动画?关键,CSS看到cx
就腿软,指望CSS怕是还不如指望自己家的阿黄。此时,要想对cx
属性操刀,势必需要JS大人出马。设置cx
+=200
,
就可以水平右移200像素了。
我把这种CSS搞不定,JS才能驱动的SVG图形变化称之为「SVG图形动画」。
三、SVG图形动画理论基础
CSS3驱动的元素动画,专业术语称为“补间动画”,指定动画的关键点,对比关键点前后差异,对变化数值做连续化处理。
实际上,任何有规律可循的web动画,包括SVG图形动画,其本质实现与上面的类似。找出前后差异,根据动画引擎做连续化处理。
还是上面那个大力神杯的例子。
其实要位移,何需动cx
的干戈,translateX(200px)
就可以满足需求。浏览器内置的动画引擎可以自动检测0~200
的位移,配合缓动函数,就有我们的动画效果了。现在问题来了,如何我们非要使用cx
属性(SVG circle
元素横坐标属性)实现呢?
1. 自己写一个简单的动画处理方法; 2.使用优秀的开源的。
出于学习而非政治,加上本身涉猎不深,因此,自己采用了策略2 – 使用之前墙裂推荐的Snap.svg.js.
其中,对于元素,扩展了个animate()
方法,可以直接补间形成动画效果。套用上面大力神杯的例子,就有代码:
circle.animate({ cx: 200 });
位移动画直接呈现,超简单。
但是,并不是所有的变化都是可以轻松补间的,尤其当属性值是有多个数值组成的时候,甚至还有特定指令的时候。
比方说下面这两个萌脸:
假设左图是起始态,右图是终止态,来,给爷来个补间!
呵呵,傻眼了吧。
显然,目前技术来看,是不可能一行代码就搞定的。要真是这样,呵呵,动画公司的那些制作人员要担心自己的饭碗了,人人都是动画师的节奏啊!
但是,理论上,上面如此复杂的图形补间也是可以实现的。
这就需要深入图形的本源了。
SVG中,规则图形,例如,足球、球门都是有特定的元素的,这些是常用图形,为的是快速方便构建。不规则图形,例如,萌脸的曲面脸蛋,C罗的嘴型,一般都是使用万能的path
元素实现。path
元素可以实现任意规则或不规则的线、面。
而这些线、面本质上就是path
元素的d
属性值的·一系列坐标参数+特定指令·构建的。因此,左萌脸的本质可能是坐标a
b c
,右萌脸的本质可能是坐标d e f
,我们只要找到两个坐标的某些关联规律,就能实现补间效果,也就是SVG图形动画效果了。
文字要开花的节奏,赶快插播了个例子:
上图Gif效果,就是对路径的某一点做有规律的运动形成的动画效果。
您可以狠狠地点击这里:一个简单的SVG路径点变化动画效果demo
这是一个五边形,我们可以使用path
的M
和L
指令进行绘制(路径指令可参见这篇文章)。
但是,刚开始,温水炖青蛙,蚯蚓钓蛤蟆,一上来就path
指令会吓着花花草草以及路过的小朋友的,因此,这里使用polyline
多边形元素,使用points
属性标记点。例如,上面的demo默认五边形的点位置是(显示器尺寸不一样,值也不一样,因此,仅示意):
<polyline points='798,432,1022,360,1160,549,1021,740,798,667' fill="rgba(255,255,255,0.85)"></polyline>
上面颜色高亮的5个坐标就是五边形的5个顶点坐标。
要实现Gif所示的效果,原理很简单,只要让第3个点水平位移就好了。
本质上就与大力神杯的那个圆圈位移是一样的。只是,大力神杯独立参数,这里是众多参数中的一个。
这里,我们无法直接对整个属性坐标做补间,我们需要使用原生的坐标动画方法。
Snap.svg.js中有个全局的animate()
方法,该方法中文文档戳这里。
语法如下:
Snap.animate(from, to, setter, duration, [easing], [callback]);
上面介绍的Element.animate()方法就是根据这个底层方法演变而来。其关系,类似于jQuery中$.get()/$.post()
和$.ajax()
的关系。
如果套用大力神杯偏移200像素的例子,则有:
Snap.animate(0, 200, function (val) { circle.attr({ cx: val }); }, 500);
Snap.animate()
方法强大之处在于这个setter
这个函数,动画引擎的核心就是计算特定时间点的特定坐标值(颜色值、尺寸值)等,而这个函数,可以实时返回当前需要的坐标值,于是,我们就可以按照特定的位置关系、运动函数算法,求得整体图形的运动坐标,从而形成完整的动画效果。
对于这里的图形变换,是非常简单的,纯粹第三个点水平位移到中心点。
套用Snap.animate()
方法就是(假设半径是200)(下面写法纯示意,不要太认真):
Snap.animate(200, 0, funciton(val) { polygon.attr({ points: "点1, 点2, (中心点x坐标 + val, 点3 y坐标), 点4, 点5" }); }, 300, mina.backout);
具体代码,您可以“右键页面→查看源代码”,不要再邮件我要源代码了,诸位!
OK, 以上就是SVG图形动画理论基础:使用动画引擎方法(如Snap.svg的Snap.animate()
方法),改变特点时间点的特定参数值,并实时刷新。
四、更实际的SVG图形动画实例
先瞅瞅Gif截图效果:
上图效果就是某实际项目中实现的效果示意。
您可以狠狠地点击这里:SVG path d路径参数变化与图形动画demo
这是一个伪翻页效果,设计师做了此效果视频。显然,设计师如此给力,身为前端,必须高保真实现。此处,CSS3显然是无能为力的,因为折角有内凹状态(见下捕捉截图),但是,对于SVG图形动画,这就是个小儿科。
画个示意图,其实实现原理相当简单:
其实跟上面的五边形效果类似,都是某一个点沿着特定的轨迹运动,图形不断渲染形成动画效果。
只是这里是4个点,点的运动轨迹是个斜线而已。
斜线的函数方程式应该都记得吧:
y = a*x + b;
我们知道SVG右上角和左下角的坐标,上面的a
和b
速速就计算出来了。不展开。
还是Snap.animate()
方法,展开的相关JS代码如下:
// 图形展开 Snap.animate(svg_width, 0, function(x) { var p4 = [x, a * x + b].join(" "); // 路径变化走起 // M p1 L p2 L p3 L p4 L p1Z path.attr({ d: "M" + [p1, p2, p3, p4, p1].join("L") + "Z" }); }, 200, mina.easein);
哈,这里为了承上启下,使用了path
路径元素。如果你想更简单实现,可以使用polyline
元素+points
属性。
简单吧~
目前为止,我们展示的两个效果都是只有一个点变化,是不是觉得没有什么挑战性啊?哈,那下面我们再看一个多点联动变化的复杂例子。
五、多点联动的复杂SVG图形动画实例
外甥点灯笼-照旧(舅),先上一个Gif,下图为缩小版:
上图这种从盒子展开以及收入盒子的效果,我称之为“潘多拉效果”——打开潘多拉的盒子。
您可以狠狠地点击这里:SVG潘多拉图形动画效果demo
第一次载入位置是固定的,如果有点击行为,则后面刷新的时候,工具栏以及Chrome图标的位置就会随机,你会发现,无论在何方,潘多拉都会以正确的方式打开。当偏移位置较大的时候,可以看到明显的边缘弧线,这是因为使用了二次贝塞尔曲线的缘故。
实现原理
原理与上面的两个例子类似,不同之处在于,这里,SVG图形的8个点要同时运动,而不是只有1个点。例如,我们盒子要收起的时候,8个点依次奔向盒子,填充路径实时渲染,就有了我们看到的效果了。
如果要具体展开,我勒个去,估计要上百行开外了。篇幅已经很长,这里简单提几个要点:
- 同样,还是依赖于Snap.svg.js的
Snap.animate()
方法,demo中有使用其回调方法。 - 只能是
path
元素,除了起始点和闭合点(同一点),其实均是二次贝塞尔曲线指令(Q
)坐标。//zxx: 贝塞尔曲线指令可参考前一篇文章“深度掌握SVG路径path的贝塞尔曲线指令”。二次贝塞尔曲线指令绘制直线只要对应方位坐标值一致就可以。 - 每一个点对应一个运动函数,本demo使用的是线性运动函数,也就是上面提到的:
y = a*x + b;
当然,如果你数学足够好,你也可以使用曲线函数,效果会更funny!
- 垂直运动,所有坐标都是跟对纵坐标也就是
y
坐标变化而变化,水平运动则是x
坐标~ - 中间的Q点并不是完全居中的,demo中是
1/4
远端偏移的,为的是更自然的小曲面效果。 - 运动的终点,也就是盒子的坐标实际上只是一条边缘线。我曾试过中心点或整体矩形,但效果都不好。
- 每个水平面(垂直运动)或垂直面(水平运动)的点是同一对应方位坐标,其运动启动时间是有先后之分的,启动时间的先后与展示的面板/弹框的尺寸相关。一言难尽,不多说了。
以上~~如果有其他问题,欢迎评论形式交流。
如果你想要源代码,直接[右键→查看页面源代码]就可以了,有超详细的注释(含实现思路),比看文章里的唠叨有用。
此潘多拉效果的核心方法已经完全封装,可以直接使用,不依赖任何JS框架库,需要IE9+浏览器支持。注意,这里只是核心方法封装(也就是动画部分),如果你想插件化,需要把一些参数提出来,主要是两个元素,偏移比例以及运动方位,相信不是难事。
六、结束语
我清楚的记得有篇文章的结束语提到了世界杯比赛,可见,我这空间至少活蹦乱跳了4年。时间嗖得一声,发现已经是2018年世界杯了。那个时候我在做什么呢?who knows! 朝着自己的目标与方向,走好脚下的路。
世界杯的每一场比赛我都看了,不要崇拜哥,因为哥看的都是进球集锦。话说我以前的同事在赌球,由于之前冷门太多,所以昨儿个全压巴西输,荷兰输,然后,今天就在办公楼顶楼吹了半晌的风。
计划半年写20篇文章的,还差两篇,只有一周的时间,周末还要回丈母娘家,怎么办呢……
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=4225