“破碎-重组-破碎” CSS3实现Lowpoly风格变形动画终极篇

6,293 阅读10分钟

在受到SVG+CSS3动画仿作草莓音乐节宣传视频的伤害后,一直想找个简单唬人的东西做一做来平复一下悲愤的心情。翻了下收藏夹,找到了一个珍藏了很久的存货,三十种濒危动物的碎片动画效果。有时候,很多创意来自于别人,别不承认,戳戳看,很惊艳 。以前写过CSS3实现lowpoly动画效果的文章,虽然同为lowpoly效果,其实那个的实现难度要比这个高的多,也更加繁复,还用到了CSS的选择器,但是,还是那句至臻名言,创意远比技艺重要,这个动物碎片的创意曾让我惊为天人。设计师小伙伴不用害怕,最终效果,如果耐心的把文章看完,应该是可以做出来的,拿去吓一吓前端也是极好的,至于前端童鞋们,你们可以跳过直接看第三部分了,或者第四部分留的大坑。不废话,直接走起。

1.第一步,无论如何你需要做一下动画底图

因为原效果有三十种动物,我当然不会闲到每一个都做一遍,所以就挑自己心中喜欢的来几个就好。为了不让各位看官失望,我会附上可复用的代码并注明增加新的图形的使用方法,想做三十张或者三百张的,随意。先拿第一张来说:

这是我在AI中几乎完全仿照原图绘制的,对于设计师来说,做这种图分分钟钟的事,你们比我溜,我是懒人做法,把底图扔上,然后比着做出了一堆三角形,这个不用太精确的,然后各个碎片按原图着色就可以了。如果看这篇文章的刚好是诸位前端大神们,嗯,去给美腻的UI妹纸买些零食吧,只能帮到你这里了。 一共有多少个三角形的碎片呢?33片,请记住这个数字,33,后面我们再说原因。
当然了,碎片数不需要一个个的去数,因为当存为SVG格式后,打开时,会有一些<polygon points="" />的标签,每个<polygon>对应一个碎片,看看行数就行了(以下这句话仅针对无代码基础的设计师小伙伴,去下个notepad打开你的SVG文件,代码的可读性会加强)。 当然了,一张图是无法实现变形的,有些地方来不得偷懒,索性把那两张都做了吧,我选了鲸鱼和青蛙。

鲸鱼的碎片只有30片,如果你看过我的其他关于变形动画的文章比如这篇 ,应该有个基础的了解,首先碎片的数量是要相等的,所以鲸鱼加了3片白云(或者确切的说是3个<polygon>)来补齐数量。

这里说明一点,无论用哪种做图软件,AI或者sketch,切记基础图形一定是三角形,不要引入任何其他形状。也就是说导出的每个<polygon poins="">里面的坐标数量是相等的,会有三组或者四组。(AI另存和导出为两种方法生成的SVG是不同的,但四组的最后一组与第一组是重复的,推荐另存为SVG格式,此坑已经踩过)

2.第二步,现在,要想办法让碎片变形了

首先,我们需要改变SVG文件的DOM结构,目前导出来的SVG文件除了一个背景的矩形<rect>标签,剩下的应该都是<polygon>标签了,举个例子,看看你的是不是和我的一样。 这是里面的任意一个碎片(比如是鸟bird),这个动画整体的思路就是每个碎片变形的组合,以上面的为例,要变形的鲸鱼whale的<polygon>标签如下:

<polygon points="W1,W2 W3,W4 W5,W6" style="fill: #…"/>  

如果是内联的样式,那CSS3可以直接黯然离场了,还谈什么动画。现在要做的是,把这些内联的属性统统扔到CSS部分里去,然后通过d:path属性的变化来得到变形动效。

@keyframes p2{
0% {d:path('MB1,B2LB3,B4LB5,B6z');fill:#…}  /*绘制bird其中之一碎片*/
100%{d:path('MW1,W2LW3,W4LW5,W6z');fill:#…} /*绘制whale其中之一碎片*/
}
#p2{animation:p2 2s ease both;}	

而在DOM结构中,只要保留一个简单的<polygon>标签,获取这个动画属性就可以了。

<polygon id ="p2" />

在用CSS属性来绘制碎片时,格式记得一定要正确,填充色比较好理解,但所有的顶点坐标在写入d:path时都要转成Mxy Lx1y1 Lx2y2 Lx2y2 z这种样式,具体不再解释了,属于基础知识。

这里无论用什么办法,总之把所有碎片对应的<polygon>points的值转化成CSS3中的d值。比如,比如,我是用的万能的excel的强大的Find公式(嵌套了多个也挺麻烦的)来完成的(从未想过,有朝一日会用excel来拼接代码……),各路大神可能会有其他方法,正则表达式什么的,我不懂,你们各显神通, 总之,结果要是标准的,这是我们变形动画的基础。至于顺序,不重要,按照默认生成的顺序来就可以了。如果不是把所有的33个碎片都列上,整个变形其CSS3和DOM结构是非常简单的。所以,这个动效,只是繁琐,远称不上复杂。

@keyframes bg{
0%{fill:#A4C5EA}
100%{fill:#73DBE0}
}
#bg{animation:bg 2s ease both;}
/*定义背景色的变化*/

@keyframes p(n){
0% {d:path('');fill:#…}
100%{d:path('');fill:#…;}
}
#p(n){animation:p(n) 2s ease both;}
/*n=1,2,3,4……33  一共需要定义33个*/

DOM结构如下

<rect width="" height="" id="bg" />
<!--背景变色动效-->
<path id="p(n)" />
<!-- n=1,2,3,4……33  一共需要33个 -->

先享受一下辛苦工作的效果:

已经具备了雏形了,记得,上面我准备了三幅图,但这里我只做了从bird到whale的变形动画,并没有启用第三张图片frog,因为这一部分只是原理,下面才是重点。

3.第三步,比原效果更优化,我们支持交互

这里感谢Java大牛Hevo的帮助,是的,JavaScript只是随随便便懂一点,在提供了变形动画思路之后,轻松丢过来几行我看不懂的javascript脚本来实现交互,顺便可以后期扩展。原网站的动效是前后点击切换,既然求助了Hevo同学,自然要做的优美动人一些。最后效果是支持任意图形之间的变形。现在,我把各部分重新放上来。javascript部分如下

$(function(){
	$("button").bind("click", function(){
		var turnTo = $(this).attr("turnTo");
		// for backgroud changing
		var bg = $("#p_bg");
		var bg_fill = bg.css("fill")
		bg.css("fill", bg_fill).css("animation", turnTo + "_bg 2s ease both")
		
		// for lowpoly changing
		$.each($("path"), function(i, n){
			var d = $(n).css("d");

			var fill = $(n).css("fill");
			
			$(n).css("d", d).css("fill", fill);
			$(n).css("animation", turnTo + "" + (i+1) + " 2s ease both");
		});
		
	})
})

这个,这个,让我怎么解释呢?就是写了个循环然后根据点击按钮不同给CSS对应赋值并调用该变形动画属性。

CSS部分如下

@keyframes bird_bg{
to{fill:#A4C5EA;}
}
/* 定义bird的背景色 */

@keyframes bird(n){
to{d:path('');fill:#…;}
}
/*定义bird碎片最终变形的效果 n=1,2,3……33*/、

@keyframes whale_bg{
to{fill:#73DBE0;}
}
/* 定义whale的背景色 */

@keyframes whale(n){
to{d:path('');fill:#…;}
}
/*定义whale碎片最终变形的效果 n=1,2,3……33*/

@keyframes frog_bg{
to{fill:#ffcffc;}
}
/* 定义frog的背景色 */

@keyframes frog(n){
to{d:path('');fill:#…;}
}
/*定义frog碎片最终变形的效果 n=1,2,3……33*/

#p_bg{
fill:#ffd480;
animation:bird_bg 2s ease both;
}
/*定义初始画面的背景*/
#p(n){
d:path('');fill:#…;
animation:bird(n) 2s ease both;
}
/*定义初始画面的路径 n=1,2,3……33*/

/* 定义按钮样式 */  
此处省略若干…… 喜欢什么样式去copy些下来吧

DOM部分

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
	width="960"  height="720" viewbox="0 0 960 720">
<rect width="960" height="720" id="p_bg"/><!--此为背景-->
<path  id="p(n)" />  <!--n=1,2,3……33-->
</svg>
<p>
<button type="button" turnTo="bird" class="">BIRD</button> 
<button type="button" turnTo="frog" class="">FROG</button> 
<button type="button" turnTo="whale" class="">WHALE</button> 
</p>

如果省略那一堆碎片的路径和填充色,整个代码读起来还是清爽干净以及轻松愉快的。

现在,这个动画效果已经up了一个档次了。关于初始画面,那个LOW,绝壁不是效果太low的意思啊,本心是想做个LOWPOLY的lowpoly效果,无奈生成的碎片超过了33片了,又懒得改其他部分(比如我可以给所有的都补充上空白路径d:path('M0,0L0,0L0,0L0,0z')(如果你了解路径的绘制规则,应该能轻易看出来这是一个虚拟路径),来补齐所有的数量,但是,懒且麻烦就算了,low就low吧。

因为gif格式比较大,受限,所以快速点击的几下,想玩的愉快的到Demo演示地址里去吧。在文末,我会贴上我的codepen演示地址。

那么设计师小伙伴们怎么拿去用呢?看一下我的demo的命名的规则,所有的元素,我会尽量保持统一的命名,比如,你要增加一张图片是sheep(我知道这是羚羊antelope,但这个单词超出认知范畴了,看不懂)。那么,要定义的有 图片背景色sheep_bg,sheep(n)的各个碎片的d:path属性,以及增加一个 turnTo="sheep" 的按钮,仅此而已。人肉亲测有效,见下图:

鉴于原网站仅支持上下顺序查看,而我们支持任意切换,所以,此处应该有掌声。

4. 这个效果可以更好,只是不知道怎么用JavaScript控制

对于这一套动画的主体部分是动物之间的变形,但仍有小细节,比如身体某个部位依然可以动。以最初的那个bird turn to whale,在变形动画之前,bird尾巴部分可动,变形结束后,whale的鳍可动,我自己做了一下效果,大概是下面这样的。

思路很简单,把需要动的部分重新定义一个动画属性,以鸟的尾巴为例,因为肢体的摆动都是有关节的,所以用了旋转的变形属性transform:rotate()

@keyframes bird_tail{
to {transform:rotate(6deg);}
}
#bird_tail{animation:bird_tail 0.5s ease-in-out 4 alternate;transform-origin:} 
/*准确定义旋转的基点transform-origin很重要,就是关节位置*/

而鲸鱼则嵌套了两个部分,整体的一个位移,**transform:translateX()**以及局部的几片组合的旋转,再设置好时间延迟,并不是很困难。

鉴于此坑太深,不打算碰。实现这种效果后,剩下的丢给了Hevo同学,看能不能用JavaScript实现两次按钮切换之间的局部变形的循环动画。

最后的最后,小结一下,对于这个动效而言,看上去逼格满满,但其实只是一个简单的d:path()属性值改变的变形效果。但因为单变多,所以产生了这种惊叹的效果。codepen地址Lowpoly transform animation,可以去预览,怎么改成自己想要的图形,我想在上面已经说的很清楚了,玩起来咯。