如何借助SVG+CSS用2个小时撸完一个网易云音乐的动效海报

因为平时也关注网易UEDC的订阅号,前几天就看到了这么一个动效,主题是《网易云音乐2018年度听歌报告》,内容是一个人在努力蹬车因为构图简单,创意又不错,所以就试了下用SVG+CSS动画实现起来的难度,大概费时两个小时左右,效率还是蛮高的,总比用AE实现起来快的多得多,下面就捋一捋实现的过程。

1. 第一步 先构图,费时1小时

画图这种事情,怎么能难得到一个美工呢。在Ai中完成,不考虑太多细节(原效果中有渐变和噪点,这里就省略了,用纯色填充来代替,毕竟人家是一个team,而我是一个人在摸索着战斗)。

图做完之后,就要进行拆解了,需要把四个部分拆出来,这四个部分就是要做动效的部分,左大腿,左小腿,右大腿,右小腿。

这四个部分为了方便下面做动效,每一个都是要放到单独的图层中的,至于其他图层顺序,就不是那么重要了。

2.第二步 给腿部增加旋转动效,费时0.5小时

做这种涉及到关节类的实现效果,其实是个通路,因为简而言之都是旋转动画,不过是增加了连动,也就是说大腿围绕关节处(即原点)的旋转时要带动小腿围绕大腿末端(小腿动画对应的原点)的旋转。因为有做舞动的机器人的基础,这里就简单了很多,不过仍然做了一点点小的优化,没有像做机器人时拆分成很多SVG,而是直接进行了嵌套。

首先,最最重要的一点,要获取下面图中这三个坐标值。这是做旋转动画时不可或缺的tranform-origin值。

大腿的旋转是非常简单的旋转动画设置了,以左腿为例,通过对比左右腿,差不多得到的数据就是大概顺时针旋转了60度,只要在CSS中定义动画参数如下:

/*大腿旋转运动的动画规则*/
@keyframes legLMove{
0% {transform: rotate(0deg)}
100% {transform: rotate(-60deg)}
}
#legL{
animation: legLMove 1s  infinite  alternate;
transform-origin:455px 235px; /*大腿旋转运动的原点*/
}
复制代码

这样就轻松的得到了大腿运动的动效。
相比之下,小腿就要复杂的多,步步拆解嘛,动画设置的思路也是解题思路,所以,先解决的第一个问题,就是小腿跟随大腿运动的问题。因为在导出SVG之前,在Ai中已经把需要增加动效的部分放置再来不同的图层里,因此,简化的腿部对应的DOM结构是下面这种

<!--左大腿-->
<g id="legL">
	<path d="M……z"/>
</g>
<!--左小腿-->
<g id="calfL">
    <path d="M……z"/>
</g>
复制代码

既然要跟随大腿运动,那好办,直接把左小腿整体部分放到大腿所在的组合<g>标签中,就能保证相同的运动了。修改后的DOM结构就变成了:

<!--左大腿-->
<g id="legL">
	<path d="M……z"/>
	<!--左小腿-->
	<g id="calfL">
    	<path d="M……z"/>
	</g>
</g>
复制代码

现在,这一步已经解决了,那么来解决下一个问题吧,如何让小腿自由的摆动。这里,为了确定小腿的旋转参数,我画了一张示意图。

这张图中,绿色的弧线就是大腿的运动轨迹,红色的弧线是小腿的运动轨迹,最终小腿部分和大腿部分几乎是平直状态,因为小腿目前是跟随大腿运动的,换句话说,它们两个处于同一个参考系中,因此,在确定小腿的摆动幅度时,不用考虑大腿的状态。(半透明的小腿部分就是最终小腿的状态)。目测,逆时针旋转,100度左右(不用那么精确)。因此,小腿部分的动画规则就出来了:

@keyframes calfLMove{
0% {transform: rotate(0deg)}
100% {transform: rotate(100deg)}
}
#calfL{
animation: calfLMove 1s  infinite alternate;
/*小腿对应的旋转原点 也就是大腿的末端*/
transform-origin:343px 220px; 
}
复制代码

这样,小腿在跟随大腿进行旋转运动的同时实现了自己的旋转动效。(这里实现的这种方法要比之前在做舞动的机械人时优化了很多)。

左腿完成了,右腿就水到渠成了,不过是一个逆向而已。记得修改DOM结构。

当然,这个蹬自行车的效果实现的并不好,看上去更像是踩踏板,有时间的话准备开一篇新的文章通过定义 offset-rotate:var(--degMove);这种在CSS中设置随路径曲率的旋转方向为变量的方法,然后javascript设置定时器改变这个值来实现(这个方法已经测试过了,js同样也可以通过 document.documentElement.style.setProperty的方法来改变SVG的CSS样式)。此为后话。

3. 第三步 增加音符的动效,费时0.5小时

为了尽可能的和原效果保持一致,这里把音符的动效一并加上了。为了方便查看,我先把无关元素暂时隐藏掉,只保留音符,大概的效果是下面这种:

音符逐渐变大并沿路径移动后淡化消失的过程。从这句话中,我们提炼出三个元素: 沿路径移动 | 变大 | 变淡,也就是说对应三种动效设置。 第一步,为了实现路径动画,我先绘制了路径,并确保每个音符落在路径上。

路径动画的CSS参数写一写:

@keyframes notePath{
0% {offset-distance:0%;}
100% {offset-distance:100%;}
}
#notePath{
offset-path:path('  '); /*绘制的路径path对应的d属性*/
animation:notePath 2s ease infinite;
}
复制代码

音符先不做缩放的处理,以中间状态的为基准,调用这个动画属性后,就可以得到沿路径运动的音符了**(再次强调,在路径动画中,一定要把元素放到画布零位置后再导出。)**:

继续,下面叠加尺寸变化,animation属性不需要做任何修改,但动画规则中要增加关于缩放,也就是 transform:scale()的定义

@keyframes notePath{
0% {
offset-distance:0%;
transform:scale(0.2); /*增加关于缩放的定义,起点缩小至原尺寸0.2*/
}
100% {
offset-distance:100%;
transform:scale(1.5); /*增加关于缩放的定义,终点放大至原尺寸1.5倍*/
}
}
复制代码

此时,音符在沿路径移动的基础上已经叠加了缩放的效果:

只差最后一步,就是透明度的变化了。通过分析动效,音符并不是在整个动画的时间过程中遵循透明度的变小的规律,而是在变化的中途开始逐渐变淡。这里的处理有两个思路,正好都说一下。第一种,直接定义在统一的动画规则中。既然是中途的变化,那我们CSS可以改成下面这种:

@keyframes notePath{
0% {
offset-distance:0%;
transform:scale(0.2);
}
50%{opacity: 1} /*在中途增加一个关于透明度的定义*/
100% {
offset-distance:100%;
transform:scale(1.5);
opacity: 0.1
}
}
复制代码

来个好玩的吧, 试试骗过浏览器。 其实浏览器是很傻的,它只会乖乖的看着你的动画规则进行解析,比如透明度这种问题,我们知道 opacity:1就是完全不透明的了,但浏览器对于opacity定义大于1时,也会按照完全不透明来解析。想到了什么没?
也就是说,我们完全不用增加一个50%的关键帧,而是写成下面这种:

@keyframes notePath{
0% {
offset-distance:0%;
transform:scale(0.2);
opacity: 2; /*透明度设置>1*/
}
100% {
offset-distance:100%;
transform:scale(1.5);
opacity: 0.1;
}
}
复制代码

对浏览器来说,它要执行的过程中,透明度opacity是这样来的,整个时间段,2→0.1,所以差不多前半段是2→1(对应透明度不发生变化),后半段1→0.1(对于透明度变小),也就满足了我们的设定。 第二种方法,虽然略显麻烦,却是很重要的一种方法。在上面的设置中,有一个问题,就是因为animation属性是一个统一定义,这里面包含的参数非常多,比如说速率,也就是我定义为ease值的参数。如果我想让透明度线性速率变化,怎么办呢?来试试给动画属性叠加多个规则

/*增加一个设置透明度变化的动画规则*/
@keyframes noteOpacity{
	50%{opacity:1}
	100%{opacity:0.1}
}
复制代码

这时,我们要把这个透明度变化的动画规则追加到animation的定义中,追加的方式很简单,逗号后面增加上新的动画规则的属性就可以了:

animation:notePath 2s ease infinite ,noteOpacity 2s linear infinite;
/*新的动画规则noteOpacity可以自由定义其他动画属性*/
复制代码

这种叠加多个动画规则(当然了,你得有个前提,这些动画规则分别定义了不同的属性变化)在一些场景下非常有用的。以这个来做一个非常简单的尝试。当我把noteOpacity这个动画规则的动画时间由2s改成0.5s,为了看出差别,路径动画的时间延迟到了5s,猜猜动效会发生什么变化?

正如图中所示,这是两个不同的动画规则的叠加效果,因此整个路径运动的过程中会有数次透明度的变化。 好了,让律动的音符和我们骑自行车的动效进行合成吧!

粗制滥造的仿作到此为止。来个小结吧,这篇文章中两个重要的部分:

  1. 对于连动效果,先把整体部分放入需要连动的元素的同级中,以便保持相同的动画设置,再另行设置单独的动画属性。
  2. 给动画属性animation增加多个不同的动画规则的方法非常非常有用。

我也是在做CSS动画的过程中,不断来优化原来总结的知识体系,以后遇到好玩有趣的案例都会做一做。至于CSS变量这个,虽然是2年前的功能,对我来说,却是新的知识点,也准备试一下和js结合之后能怎么玩起来,毕竟要比通过innerHTML方法向CSS文件中添加<style>标签和内容要简单明朗的多。

猜你喜欢

转载自juejin.im/post/5c7f6e796fb9a049f154f59d