前端做动画有哪些方式?各有什么优缺点?
css3
- transition
- animation
利用css3上述两种动画可以满足我们都日常需求,但是有很多效果它做不到,比如下面我们要说都一个loading
js + css
通过js去动态去改变css属性也可以实现动画,只是在上面css3动画都基础上我们还要去操作dom
canvas + js
利用canvas画图更新每一帧做动画,很多h5游戏也是这么做的,它更适合游戏这样复杂等场景
gif
利用photoshop等工具制作gif,在项目中直接引用。在需求特别紧急,而且动画又特别复杂的情况下,自己没有把握按时实现效果,或者代价太大,真的,别犹豫,上gif图片。
gif的不足就是修改它就得去换图,并且质量差,容易虚
svg
都知道svg是矢量图,放大不失真,却不是谁都知道svg可以做动画,比如最近跟我合作的设计师….
svg可以用它的animate标签来做动画,也可以用css3做动画。
用svg做动画我们可以随意缩放,并且无需操作dom。
动手做一个svg动画
今天主要要说的也就是怎么利用svg做动画,下面我们会做这样一个loading
咱们需要的loading要求为:在父容器为29*29时,直径(含边框)为29,边框宽为4,边分为深灰和浅灰两段,
这两段分别执行长短变化的动画,整体有旋转动画。深灰色部分长度
改变父容器宽高,loading能等比缩放。
先介绍一种简单的方法,如果你的交互设计师使用AE(Adobe After Effects)设计的动画效果,那么你可以推荐它使用bodymovin这款插件帮你直接倒出svg动画,
可以直接导出demo,但是这个svg是要依赖bodymovin.js库去播放的🐶🐶,可参考 大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画
言归正传:怎么实现上面这个动画?
首先了解一下原理
1 | <svg class="loading" width="100%" height="100%" viewBox="0 0 29 29"> |
首先认识一下上面svg中的基本属性:
cicle用来画一个圆,fill="none"
将圆掏空为边框,stroke-width
为边框宽度,cx
和cy
属性定义圆点的 x 和 y 坐标,r为圆的半径
viewBox
viewBox
的四个参数分别代表:最小X轴数值;最小y轴数值;宽度;高度。
前两个暂时用不到,可理解为除对内部svg做整体位移,一般都是0 0,暂时先不做解释,重点关注后两个参数,可理解为svg的画布大小,会将内部按照在此宽高基础上的占比,放大至svg的宽高。
不理解的话可以看 理解SVG viewport,viewBox,preserveAspectRatio缩放/SVG之ViewBox为了使svg内部能撑满,这里设置
viewBox="0 0 29 29"
- stroke-dasharray
重点来了,stroke-dasharray
在SVG中表示描边是虚线,两个值,第一个是虚线的宽度,第二个是虚线之间的间距。有点像css中dashed的border。我们就是利用它也画我们弧形的边框,以及后续控制边框长短。
- stroke-dashoffset
定义虚线描边的偏移量。
你可能需要看一下纯CSS实现帅气的SVG路径描边动画效果
理解动画过程
先画出圆,这一步相对简单,主要是使用合适的viewBox。另外svg的css记display:block
,否则在容器较小的会有莫名的边距的。1
2
3
4
5
6<svg class="loading" width="100%" height="100%" viewBox="0 0 29 29">
<circle class="c1" cx="14.5" cy="14.5" r="12.5" fill="none" stroke-width="4"
stroke="#b1b1b1" />
<circle class="c2" cx="14.5" cy="14.5" r="12.5" fill="none" stroke-width="4"
stroke="#c7c7c7" />
</svg>
深灰色圆弧c1的长度变化动画
c1的动画相对简单,stroke-dashoffset
默认为0即可,主要是利用stroke-dasharray
控制圆环弧度的长。
通过的两个参数的意义,我们可以推测,c1的stroke-dasharray
第一个参数为1,第二个参数为周长2πr的值时刚好只剩一个小边,第一个参数为周长,第二个参数也为周长
时刚好成为360度的圆环。尝试发现确实是。
我们假定stroke-dasharray:a b
即第一个参数为a,第二个参数为b,则b为周长,a从1向上加即可得到顺时针的c1的长度动画。如果想要按照需要的长度区间,则可以根据周长的占比计算。
比如:长度在1.832s内从78%到31%再到78%的动画,动画曲线cubic-bezier(0.18, 0, 0.58, 1)。1
2
3
4
5
6
7
8
9
10
11.c1{
animation: long2 1.832s cubic-bezier(0.18, 0, 0.58, 1) 0s infinite;
}
@keyframes long2 {
0% {
stroke-dasharray: 60.6 78;
}
50% {
stroke-dasharray: 23.9 78; }
100% {
stroke-dasharray: 60.6 78; } }
浅灰色圆弧c2的长度变化动画
假定c2的stroke-dasharray:a b
即第一个参数为a,a的长度由占比周长得到,第二个参数为b,从上面知道b的值一样需要是圆的周长。storke-dashoffset: c
参数为c。
由于c1在动画过程中长度会变,这个过程c1的起点不变,尾巴在顺时针变长变短。而c2需要紧跟c1,那么我们就需要让c2往反方向么也就是逆时针做长度变化的动画,控制c2的storke-dashoffset
值让它紧跟c1,
当c == 2 a的时候,c2刚好与c1衔接。(c2的stroke-dashoffset再减一个1 免得连不上)
比如:长度在1.832s内从12%到4%再到12%的动画,动画曲线cubic-bezier(0.18, 0, 0.58, 1)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17.c2{
animation: short 1.832s cubic-bezier(0.18, 0, 0.58, 1) 0s infinite;
}
@keyframes short2 {
0% {
stroke-dasharray: 9.36 78;
/*12*/
stroke-dashoffset: 17.72;
/* 9.36*2 - 1 */ }
50% {
stroke-dasharray: 5.46 78;
/*4*/
stroke-dashoffset: 9.92; }
100% {
stroke-dasharray: 9.36 78;
/*12*/
stroke-dashoffset: 17.72; } }
旋转动画
旋转动画有多种实现方式
- css3 animate动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15.loading {
animation: rotate 1.832s linear 0s infinite;
display: block;//一定记得svg转块,不然小的时候莫名的被父元素挤出来
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(360deg);
}
100% {
transform: rotate(720deg);
}
}
- svg内部animate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<svg class="full-loading" viewBox="0 0 29 29">
<circle class="c1">
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1.832s"
from="0 14.5 14.5"
to="720 14.5 14.5"
repeatCount="indefinite" />
</circle>
<circle class="c2">
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1.832s"
from="0 14.5 14.5"
to="720 14.5 14.5"
repeatCount="indefinite" />
</circle>
</svg>
这里要注意,下面这样不行:1
2
3
4
5
6
7
8
9
10
11
12<svg class="full-loading" viewBox="0 0 29 29">
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1.832s"
from="0 14.5 14.5"
to="720 14.5 14.5"
repeatCount="indefinite" />
<circle class="c1"/>
<circle class="c2"/>
</svg>
至此,loading已完成,结果可以看最上面。
做成一个svg文件
上面我们虽然完成了svg动画,但是是分为css和svg两部分,能不能合成一个svg文件呢?
我们这么把css嵌入svg文件呢?
详细内容你可以看看SVG element reference
1 | <?xml version="1.0"?> |
这里的旋转用的animate标签,因为animate的rotate动画拿进来不行,很诡异。