关于动画的几个问题总结

1. 前言

大家应该经常会看见一些企业的运营小活动网页,这些活动通常只有几屏内容,每切换一张,里面的内容,图片会做相应的动画飘入。这就是我今天要讲的内容。

2. 动画实现的几种方法

  1. CSS3属性transition
  2. 利用setTimeout间隔的移动节点
  3. jQuery的animate方法
  4. rquestAnimationFrame

2.1 transition

主要用在单页面,比较独立的动画上面

1
2
3
4
5
6
7
8
9
10
11
12
13
div{
width:100px;
height:100px;
background:red;
animation:myfirst 5s;
}

@keyframes myfirst{
0% {background:red;}
25% {background:yellow;}
50% {background:blue;}
100% {background:green;}
}

2.2 setTimeout

优点:可以用CSS3属性做动画,可以具体规定多个动画的先后顺序啊,具体实现啊之类的
缺点:setTimeout以间隔时间来拖动节点,和屏幕的刷新屏幕不一致,会导致错位的感觉

1
2
3
4
5
6
7
8
var el = document.getElementById("target");
for ( i = 0; i < 200; i ++){
setTimeout ( function(){
var left = el.style.left ? el.style.left : 0;
left = parseInt(left) + 1;
el.style.left = left + "px";
} , i );
}

2.3 animate

优点:可以具体规定多个动画的先手顺序,具体的实现过程
缺点:不能改变CSS3属性,支持的属性有限

2.3.1 .animate()方法有两种形式

1 第一种形式接受4个参数
a. 一个包含样式属性及值的映射
b. 可选的速度参数——既可以预置的字符串,也可以是一个毫秒数值
c. 可选的缓动类型
d. 可选的回调函数

1
2
3
4
5
6
animate({property1: 'value1', property2: 'value2'},
speed, easing,
function(){
alert("The annimate is finished.");
}
);

2 第二种形式接受2个参数,一个属性映射和一个选项映射

1
2
3
4
5
6
7
8
9
10
.animate({propertoes}, {options})
.animate({property1: 'value1', property2: 'value2'}, {
duration: 'value',
easing: 'value',
complete: function(){
alert(" The animate is finshed.");
},
queue: boolean,
step:callback
});

2.3.2 并发与排队效果

2.3.2.1 当为同一组元素应用多重效果时,可以通过连缀这些效果轻易地实现排队

1
2
3
4
5
6
$('button')
.fadeTo('fast',0.5)
.animate({left:'10px'},'slow')
.fadeTo('slow',1.0)
.slideUp('slow')
.slideDown('slow');

1 越过队列
上面的动画是一个一个执行的,如果有些动画我想要一起执行,可以简单地把它们组合到一个.animate()方法中,但如果想要组合的两个动画的duration不一样,那么就得用第二种形式的动画了

1
2
3
4
5
6
7
8
9
10
11
12
$('button')
.fadeTo('fast',0.5)
.animate(
  {left:'10px'},
  {
    duration:'slow',
    queue:false //把该选项设置为false即可让当前动画与前一个动画同时开始
  }
)
.fadeTo('slow',1.0)
.slideUp('slow')
.slideDown('slow');

2 手工队列
为一组元素应用排队效果的最后一个需要注意的问题就是排队不能自动应用到其他的非效果方法,如.css()
假设上面的程序,我想要在slideDown前将背景色修改为红色,如果直接

1
2
3
.slideUp('slow')
.css({backgroundColor:'red'})
.slideDown('slow')

背景色会立即改变,不会等到其他动画执行完了再执行。
把非效果方法添加到队列中的一种方式,就是使用.queue()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$('button')
.fadeTo('fast',0.5)
.animate(
  {left:'10px'},
  {
    duration:'slow',
    queue:false
  }
)
.fadeTo('slow',1.0)
.slideUp('slow')
.queue(function(next){ //传递一个回调函数
  $('button').css({backgroundColor:'red'});
  next(); //添加的这个next()方法可以让队列在中断的地方再接续起来,如果不使用next(),动画就会中断
})
.slideDown('slow');
//或者
$("div").slideUp().queue(function(){
$("div").css('background', '#f00').dequeue();//dequeue()可以让队列在中断的地方再接续起来
}).slideDown();

3 回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('button')
.fadeTo('fast',0.5)
.animate(
  {left:'10px'},
  {
    duration:'slow',
    queue:false
  }
)
.fadeTo('slow',1.0)
.slideUp('slow',function(){
  $('p').eq(2).css({backgorundColor:'red'}); //用回调函数,不需要用next()
})
.slideDown('slow')

2.3.2.2 处理多组元素

1
2
3
$('button').click(function(){
  $('p').eq(2).slideUp().next().slideDown(); //.next()表示下一个同辈元素
})

此时,两个段落的动画是同时执行的,如果想要一个他们按顺序执行,需要用排队回调函数

1
2
3
4
5
$('button').click(function(){
  $('p').eq(2).slideUp('slow',function(){
    $(this).next().slideDown();
  })
})

2.3.3 关于效果执行顺序的总结:

1 一组元素上的效果
当在一个.animate()方法中以多个属性的方式应用时,是同时发生的;
当以方法连缀的形式应用时,是按顺序发生的(排队效果)——除非queue选项值为false;
2 多组元素上的效果
默认情况下是同时发生的;
当在另一个效果方法或者在.queue()方法的回调函数中应用时,是按顺序发生的(排队效果)。

2.3.4 注意事项

1 提示:可以用 animate() 方法来操作所有 CSS 属性吗?
是的,几乎可以!不过,需要记住一件重要的事情:当使用 animate() 时,必须使用 Camel 标记法书写所有的属性名,比如,必须使用 paddingLeft 而不是 padding-left,使用 marginRight 而不是 margin-right,等等。
同时,色彩动画并不包含在核心 jQuery 库中。
如果需要生成颜色动画,您需要从 jQuery.com 下载 Color Animations 插件

2 什么叫效果方法
就是jquery封装起来的显示隐藏、淡入淡出、滑动、动画之类的方法

2.4 requestAnimationFrame

这里我总结不出来什么东西,只能参考相关文章,都讲解的非常清楚了
更好的逐帧动画函数 — requestAnimationFrame 简介
性能更好的js动画实现方式——requestAnimationFrame
requestAnimationFrame for Smart Animating

为什么会有requestAnimationFrame的存在呢?因为无论是setInterval()还是setTimeout都不是十分精确。为它们传入的第二个参数,实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他的任务,那动画代码就要等前面的任务完事之后才能执行。这样就造成了一定的堵塞。

优点:
1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。

requestAnimationFrame的用法

1
2
3
4
5
6
7
8
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();

对requestAnimationFrame更牢靠的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(function() {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}

if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};

if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());