先放上效果图,再说实现方法。
实现的过程比较简单,不过大量用到了我在SVG变形动画里提到的虚拟曲线 c0,0,0,0,0,0,需要补充相关知识的移步 juejin.im/post/59195c… 这篇文章。
这个案例因为用到了一个元素叠加多个动画,所以还是具有代表性的,而且表情元素可以复用(见文章最后)。说一下实现的过程(思路解剖)
1.动静分离
无论何种形式的SVG动图,第一步自然离不开我们的“动静分离大法”。静态图分离出来如下:
AI里放到命名为“base”的图层里。这个导出SVG后是可以不用理会的。
这个动效里,涉及的动效元素有三个,左眼、右眼和嘴巴。表情一共有四种,分别为初始的可爱cute,笑smile,悲伤sad以及晕faint,因此,为了获得路径,需要在AI里依次建立这四个表情的图层。对应眼睛的动画有四个,三个表情变化(需要路径变形动画完成)外加旋转动画。对应嘴巴的动画为三个变形动画,如下图所示。
理清动画原理,就可以开工了。
基础代码如下:
<g id="base">
…对应静态底图的代码…
</g>
<g class="emotion">
<path id="left"/>
<path id="right"/>
<path id="mouse"/>
</g>复制代码
"left"为左眼路径(由于悲伤sad的左右眼是对称关系,所以没有定义一组动画多次引用);"right"为右眼路径;"mouse"为嘴巴路径。为了方便定义描边属性,我把所有的表情元素放到了一个组里。这样我就可以通过定义CSS属性
.emotion path{fill:none;stroke:#615658;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10}复制代码
来赋给所有的表情元素相同的描边效果了。
2.第一个动画的实现
先说眼睛从圆形变成弯弯曲线的过程。首先确定的是,肯定是用了变形动画。弯弯曲线只需要一段路径就可以实现,而圆形的组成路径有四段路径,因此我们需要改造的就是路径少的曲线,方法也很简单,只需要给其增加三个锚点,使其变成四段路径就可以了。另外圆形路径也要用剪刀剪成开放路径。其他要注意的细节调整方法不再详细说了,变形动画的三篇文章里有详细介绍。
@keyframes deformLeft1{
0% {d:path('')} /*圆形路径*/
100%{d:path('')} /*笑时的弯弯路径*/
}
#left{animation:deformLeft1 1s ease}复制代码
这样就实现了左眼的变形动画。
这个动画过程没有给路径少的曲线直接增加虚拟曲线是因为试过之后效果不好。
嘴巴实现的过程是直线变曲线的过程,AI直接导出的直线一定是下面这样的
<line x1="" y1="" x2="" y2=""/>复制代码
而微微上翘的嘴巴是对应路径标签<path>
。我们要做的,就是根据直线起点(x1,y1)终点(x2,y2),(水平直线的纵坐标y1与y2是等值的)将其转化成路径标签,方法也很简单,对应d值Mx1,y1c0,0,0,0,(x2-x1),0。
转化后我们就可以给嘴巴也定义同样的路径变形动画了
@keyframes deformMouse1{
0% {d:path('')} /*直线转化后的路径*/
100%{d:path('')} /*微微上翘的嘴巴的路径*/
}
#mouse{animation:deformMouse1 1s ease}复制代码
至此,完成了第一个动画过程。
在路径的改造过程中,请用任意一个方法保留未修改的路径,后面揭晓原因。
3.第二个动画的实现
在由微笑变伤心的过程中,因为表示伤心的眼睛是两条直线折线绘制的,这时导出的标签是<polyline points=""/>
,当然了,如果你对贝塞尔曲线比较熟悉的话,完全可以根据坐标值直接来转化成小c开头的路径绘制方法,不熟悉的话就把直线改成带很少很少很少一点点手柄的曲线吧。
我们在上一个动画完成时,为了配合圆形,把眼睛弯弯的一条路径改成了四条,那么到了这里怎么办?难道再把伤心的眼睛也切成四份,好吧,即使这里可以这么操作,那最后晕晕眼睛的螺旋线?那可是N条路径组成的,难道都要以最终数量最多的路径为准,加锚点加到手软抽筋?!
所以说才要保留一份未修改的路径嘛。我们直接用原始的路径来进行改造,因为伤心的眼睛是两条路径,所以给原始路径增加一个锚点,使路径由一条变成两条就可以啦。无论是路径上添加了几个锚点,视觉上无差,所以动画才能实现无缝拼接。
@keyframes deformLeft2{
0% {d:path('')} /*变身两条路径的弯弯的眼睛*/
100%{d:path('')}/*悲伤的眼睛*/
}复制代码
我们是给一个元素附加多个动画,语法如下:
#left{animation:deformLeft1 1s ease,deformLeft2 1s ease 1s}复制代码
后面依次累加,用逗号,隔开就可以了,因为第一个动画我设置了1s的完成周期,所以第二段动画deformLeft2最后的1s表示延迟1s后执行。
之所以没有采用定义多个关键帧的方式来实现多个变形动画,一个是考虑到最后的螺旋线路径数量太多,当然,最重要的是每一个变形动画完成我都采用了ease函数,使每段动画增加一点停顿感。
对于第二个动画而言,嘴巴的变形很简单,就不再赘述了。
4.第三个动画的实现
这个动画变形过程路径的数量差异比较大,当然了,掌握了奇淫技巧的我们怎会被这个问题难住,悲伤的眼睛只有两段路径,而螺旋线眼睛足足有七段路径,差的五段路径让我们万能的虚拟曲线c0,0,0,0,0,0来实现就好啦,so easy!
这里因为圈圈太多,所以描边粗细stroke-width也顺便改一下。
@keyframes deformLeft3{
0% {d:path('')} /*后面补5个c0,0,0,0,0,0*/
100%{d:path('');stroke-width:4}
}复制代码
继续往后面追加定义的动画deformLeft3咯
#left{animation:deformLeft1 1s ease backwards 1s,deformLeft2 1s ease 1s,deformLeft3 1s ease 2s forwards}复制代码
这里唯一的不同是我加了 forwards,这个属性值对应的动画属性animation-fill-mode,用来定义结束后动画的状态,forwards为动画完成后保持最后一个属性值。
为什么要添加这个属性呢?因为从一开始,我们的眼睛和嘴巴元素在代码中是没有体现的,全凭动画属性绘制路径来添加,而眼睛的下一组动画为旋转,如果变成圈圈眼之后不定义结束后动画状态,在旋转动画时,动画元素为空。
嘴巴的变形动画也星对比较简单,只需要给表示悲伤的向下弯曲的路径增加锚点,补齐撇嘴对应的路径数量就好啦。
5.第四个动画的实现
眼睛的旋转动画实现起来很简单,不过要定义一下旋转的中心点transform-origin,不定义为默认以画布左上角原点为中心点。
@keyframes deformLeft4{
0%{transform:rotate(0); transform-origin:}
100%{transform:rotate(360deg); transform-origin:}
}复制代码
这个值直接通过AI获得。
因为眼睛旋转的时候,嘴巴是不动的,所以在调用嘴巴的最后一个动画时也要叫上forwards属性。
#mouse{animation:deformMouse1 1s ease,deformMouse2 1s ease 2s,deformMouse3 1s ease 3s forwards}复制代码
好了,以上就是全部动画的实现过程,那么做完这么一套动态变化的表情能用在什么地方呢?这就是我们“动静分离大法”玄妙之处,你只需要改一下静态底图,就能获得各种不同效果。而且任意改颜色,简直爽歪歪。
6.不同底图的应用
比如我曾经做过一组方形的表情图标,是下面这种
现在,我想让第一排第二个小心心图标变身动态表情,只要把我们的动画效果叠加上,再通过SVG的width、height、viewBox属性值的设定进行缩放,就得到了下面这个方形表情:
其他应用就不再一一举例了,就酱。