最近我们的设计师反馈想做如下的加载动画。但是要么导出的效果好的GIF体积很大,看完超过8M,要么就是小GIF效果特别不明显。然后看了下效果,发现用SVG动画实现应该比较简单,于是就让设计师把原稿导出成SVG再处理。由于顺丰不支持插入视频,您可以访问腾讯视频网址https://v.qq.com/txp/iframe/p...来查看设计师期待的动画视频。Airbnb有一个Lottie工具,用于将AE动画脚本转换为SVG动画。通过其AE插件Bodymovin,可以将动画信息和素材以JSON格式导出。然后在网页上使用bodymovin.js动画播放库加载JSON素材,完成动画转换。具体教程请参考Youtube视频《How to export an animation with Bodymovin》。使用Bodymovin确实很方便,但是由于设计者需要的效果比较简单,所以真的没有必要为了这个效果每次加载一个几十KB的基础库和JSON文件。所以我这里基于SVG+CSS动画实现了,最终效果如下。最终大小只有6KB,gzip之后会更小。跟着我一步一步来实现吧!这里我就不详细描述每个步骤的基本原理了。如果你想了解SVG动画的基础知识,可以看我之前的文章《SVG 动画实践》。动画拆分分析发现,该动画主要使用了平移、旋转、透明度、宽度、颜色属性变化等动画效果。所有这些都可以通过CSS3动画来实现。剩下的,我们需要先将这些动画拆分出来,分别实现。最后将它们结合起来,通过一定的时间达到一个完整的效果。这里我最终将运动效果拆分为以下几个部分:外圈的波纹效果外圈的波纹效果1外圈的波纹效果2外圈的波纹效果3主体的拉伸绿色部分平移拉伸主体绿色部分平移拉伸主体绿色部分下平移拉伸主体白球褪色效果白色褪色效果平移拉伸主体横条主体旋转蓝球旋转效果每个个体我们都需要处理动画效果,所以我们需要对导出的SVG的元素进行组织,对需要操作的元素进行分组标记。由于Sketch导出的SVG文件会包含较多的冗余元素,所以我一般会先通过svgo等工具对内容进行优化,然后再手动处理SVG。这里推荐一下张新旭老师写的SVG在线压缩合并工具,直接把SVG代码贴上就行了,非常简单。下面是SVG整体结构的示意图代码。你可以看到我的对SVG中的元素进行了重新排列,对需要操作的元素添加id属性,方便直接使用CSS选择器选择对象进行操作。另外,所有需要一起操作的元素也使用分组标签进行了封装。外圈的波纹效应外圈的波纹效应本质上是圆的半径逐渐扩大,并且伴随着一个圆的边界变窄的过程。三个圆圈中最里面的一个不需要移动,只需要移动最后两个。从设计稿中得到结束帧的状态后,制作这个动画就比较容易了。#track-circle-2{动画名称:wave1;动画计时功能:缓入缓出;动画持续时间:6s;动画迭代次数:无限;}#track-circle-3{动画名称:wave2;动画计时功能:缓入缓出;动画持续时间:6s;动画迭代次数:无限;}@keyframeswave1{50%{stroke-width:4;r:219px;}}@关键帧wave2{50%{笔画宽度:3;r:274.5px;}}预览地址:https://code.h5jun.com/xovew/...主体的拉伸运动是整体比较复杂的部分是的,但是通过拆分,我们发现实现起来比较简单,先实现内部元素的平移,再添加整体旋转效果。翻译没什么好说的。唯一的麻烦是计算通过起始帧和结束帧的位置要移动的距离。图中最后白线标出的位置就是我们需要的平移位置。#top-triangel{动画:topmove缓入缓出2s无限;}#shadow{动画:shadowhidelinear2sinfinite;}#bottom-triangel{动画:bottommove缓入缓出2s无限;}#right-triangel{动画名称:rightmoveease-in-out2sinfinite;}@keyframestopmove{从,到{转换:翻译(0、0);}50%{转换:翻译(-31px,-30px);}}@keyframesbottommmove{从,到{转换:翻译(0,0);}50%{变换:平移(-31px,30px);}}@keyframesrightmove{从,到{变换:翻译(0);}50%{转换:翻译(29px);}}@keyframesshadowhide{30%,70%{不透明度:0;}}对了,别忘了我们刚才的动画分割中也有白球和白横条的淡入淡出效果。淡入淡出的效果可以使用opacity透明度来实现,但是这里除了淡入淡出还有一个大小的变化,用呼吸效果来表达可能更合适。圆的大小是修改半径,横条的大小可以直接修改宽度。#white-ball{动画:balltransparentease-in-out2sinfinite;}#white-line{动画:linetransparentease-in-out2sinfinite;}@keyframesballtransparent{50%{不透明度:0;变换:比例(0);}}@keyframeslinetransparent{50%{不透明度:0;宽度:0px;}}根据刚才的动画分割,我们还剩下主体部分的旋转一圈。做这部分时要记住两件事。首先,默认旋转是基于SVG画布的左上角,旋转一般是基于中心,所以记得设置transform-origin为中心点。其次,Sketch导出的SVG会有大量的translate()翻译属性操作。可能是一开始设计师在某个位置画的,后来觉得不合适动一下。在SVG中,它会通过平移Reflected进行变换。这时候如果我们直接使用transform进行变换,实际上会覆盖掉它们原来的平移,从而导致之前设置的旋转中心不正确的问题。所以在这种情况下,就需要使用联合变换,只需将之前的翻译变换添加到CSS变换中即可,这也是代码中多了两个translate()的原因。#main{动画:mainrotatelinear6sinfinite;transform-origin:centercenter;}@keyframesmainrotate{来自{transform:translate(0,0)rotateZ(0deg)translate(-72px,-42px);}to{transform:translate(0,0)rotateZ(360deg)translate(-72px,-42px);}}下面是最终效果。怎么样,是不是觉得胜利已经不远了!预览地址:https://code.h5jun.com/dahag/...蓝球公转效果动画分割的最后一步就是蓝球公转效果。从上面我们知道我们可以通过设置旋转中心来进行旋转。自转是围绕自己转的,所以自转的中心就是自己的圆心,而公转是围绕太阳转的,所以自转的中心就是太阳的中心,对应我们动画中整个画布的中心。这里我也用了turn,CSS的另一个单位表示angle,代表圈数,turning°表示转一圈。除了turn之外,CSSangle还有两个单位:grad梯度和rad弧度。grad就是把一个圆分成400等分,转动360°代表400grad。rad弧度和我们数学上的弧度表示基本一致,一个圆一共是2πrad。#blue-ball{动画:线性旋转6s无限;transform-origin:centercenter;}@keyframesspin{来自{transform:rotate(0turn);}到{变换:旋转(1转);}}预览网址:https://code.h5jun.com/kiqi/e...后记最后把上面的代码放在一起就可以实现文章开头的动画效果了。是不是很简单?另外SVG标签也支持嵌入