这篇文章发布于 2014年06月24日,星期二,17:41,归类于 SVG相关。 阅读 149878 次, 今日 1 次 24 条评论
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
(本篇完)
- 深度掌握SVG路径path的贝塞尔曲线指令 (1.000)
- SVG任意图形path曲线路径的面积计算 (1.000)
- SVG任意基本图形转path路径 (0.631)
- 文字沿着不规则路径排版布局的实现 (0.423)
- 使用CSS offset-path让元素沿着不规则路径运动 (0.379)
- 贝塞尔曲线与CSS3动画、SVG和canvas的基情 (0.369)
- canvas实现iPhoneX炫彩壁纸屏保外加pixi.js流体动效 (0.324)
- 这回试试使用CSS实现抛物线运动效果 (0.324)
- 任意两个点的曲线连接JS算法 (0.324)
- 致设计师:图标图形制作-路径为王 (0.297)
- 搞懂SVG/Canvas中nonzero和evenodd填充规则 (RANDOM - 0.252)
666
大神很厉害,在这里看到了很多常用知识点~
我居然又复习了一遍一次函数
已知某个一次函数的图象与x轴、y轴的交点坐标分别是(-2, 0)、(0, 4),则这个函数的解析式为_____。
解:设一次函数解析式为y=kx+b
由题意得 ,
故这个一次函数的解析式为y=2x+4。
读书的回忆 嘻嘻
今天连看N章,文章精彩,技术硬,但是让我印象最深刻的是你是一个被程序耽误的段子手…
list.addEventListener(“mouseover”, function() {
// 鼠标移入,图形展开
Snap.animate(svg_width, 0, function(x) {
var p4 = [x, a * x + b];
// 路径变化走起
// M p1 L p2 L p3 L p4 L p1Z
path.attr({
// d: “M” + [p1.space(), p2.space(), p3.space(), p4.space(), p1.space()].join(“L”) + “Z”
d:”M50 50 L100 100, 200 250,50 50Z”
});
}, 5000, mina.easein);
});
list.addEventListener(“mouseout”, function() {
// 鼠标移出,图形收起
Snap.animate(0, svg_width, function(x) {
var p4 = [x, a * x + b];
// 路径变化走起
// M p1 L p2 L p3 L p4 L p1Z
path.attr({
// d: “M” + [p1.space(), p2.space(), p3.space(), p4.space(), p1.space()].join(“L”) + “Z”
d: “M0 0 100 100 200 250L z”
});
}, 5500, mina.easeout);
});
这样动画直接一闪而过,没有缓动效果,这是为什么呢?
怎么留言不成功?
大神讲的真好,就是看起来还真的挺费劲的,大神在14年就已经把svg弄得很明白了,而我才刚刚开始前端的学习路程,任重而道远呀,希望有天可以和大神一样博学
刚接触前端不久,老师让做一个监控功能,大概意思是:想实现,svg以时间点为元素的波形图,时间变化时图形会变化,思路是什么?谢谢啊!
挺好的学习资源,刚接触,不知不觉,学了快一个月了。用心学,还要用心学数学。
数学不好,还真不行!
有丈母娘了~伤心 ~继续多发文哦,应届毕业生表示学习资源有限
自从做前端以来,一搜一些前端疑难问题,百度后,总是在这里找到了答案。。。大榭,楼主,提供这个平台,让我们受益!
请问你那个gif是用什么软件录的
@BlwooSky 网上找的,若水gif
容许我在这里冒昧的提个意见,博主前端技术很是不错,但是这个博客是否因为搭建得比较早的缘故,是否该改个版了,一方面可以将自己的前端技术适当应用上,另一方面可以在排版和布局上更加简洁合理些
我觉得博主这个界面挺好的啊,很文艺啊
张前辈的SVG科普很好,小乐图客的矢量截图功能,就使用了SVG的技术,可否分享一下,SVG色相滤镜,或CSS3色相滤镜 -webkit-filter: hue-rotate(数字deg),如何通过Canvas操作像素(矩阵变换)来等价模拟?w3官网上的矩阵参数有误(在Chrome中,CSS3滤镜与Canvas实现的结果不一致,其它滤镜基本符合)。谢谢了!
哈哈 最近我也在研究svg的东西, 楼下的svg虽然是很早的技术, 可是h5让内联svg重新点燃了活力, 一直感觉用svg写页面要比 html酷, 可是svg做动画还是不如canvas性能好的 ,svg的好处在于矢量图和与js的交互方便.
SVG 真的是很早很早的技术了
“上图这种从盒子展开以及收入盒子的效果,我称之为‘潘多拉效果’——打开潘多拉的盒子。”
嘛,你知道乔布斯怎么命名这种效果的么?(类似这个但比这个更华丽。)OS X 控制中心中称其为“神奇效果”……
汗……
P.S. 吐嘈时间:神奇的动画,动了又动。大快人心的动画,大快所有人心的华丽动画。全新动画方案已发布,全新动画方案也已发布。真的动,动起来。先进的动画效果,更加先进。
svg,我觉得以后会大量运用动画中的,新技能get!
牛逼
好文章。请前辈喝杯饮料。