大熊君学习html5系列之------requestAnimationFrame(实现动画的另一种方案)

一,开篇分析

Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例,

让大家一步一步的体会"h5"能够做什么,以及在实际项目中如何去合理的运用达到使用自如,完美驾驭O(∩_∩)O~,好了,废话不多说,直接进入今天的主题,

今天主要讲的是“requestAnimationFrame API”及在客户端浏览器中的作用,并且会引入一个实际的例子做为讲解的原型范例,让我们先来看看“requestAnimationFrame API”:

  其实在web开发中,我们实现动画效果的可选方案已经很多了:

  1,你可以用CSS3的animattion+keyframes。

  2,你也可以用css3的transition。

  3,你还可以用通过在canvas上作图来实现动画,也可以借助jQuery动画相关的API方便地实现。

  4,你还可以使用最原始的“window.setTimout()”或者“window.setInterval()”通过不断更新元素的状态位置等来实现动画,前提是画面的更新频率要达到每秒60次才能让肉眼看到流畅的动画效果。

  5,哈哈哈!现在又多了一种实现动画的方案,那就是还在草案当中的“window.requestAnimationFrame()”方法。

 

    技术分享

 

 

 

 

  requestAnimationFrame是什么?

    在浏览器动画程序中,我们通常使用一个定时器来循环每隔几毫秒移动目标物体一次,来让它动起来。如今有一个好消息,浏览器开发商们决定:“嗨,为什么我们不在浏览器里提供这样一个API呢,

这样一来我们可以为用户优化他们的动画。”所以,这个requestAnimationFrame()函数就是针对动画效果的API,你可以把它用在DOM上的风格变化或画布动画好或WebGL中。

  使用requestAnimationFrame有什么好处?

    浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。

比如,通过requestAnimationFrame(),JS动画能够和CSS动画/变换或SVG SMIL动画同步发生。另外,如果在一个浏览器标签页里运行一个动画,当这个标签页不可见时,

浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

  requestAnimationFrame的用法

   先来看一个简单的小栗子:

   模拟一个进度条动画,初始div宽度为1px,在step函数中将进度加1然后再更新到div宽度上,在进度达到100之前,一直重复这一过程。

 

<div id="test" style="width:1px;height:17px;background:#0f0;">0%</div>
<input type="button" value="Run" id="run"/>

window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var start = null;
var ele = document.getElementById("test");
var progress = 0;

function step(timestamp) {
    progress += 1;
    ele.style.width = progress + "%";
    ele.innerHTML=progress + "%";
    if (progress < 100) {
        requestAnimationFrame(step);
    }
}
requestAnimationFrame(step);
document.getElementById("run").addEventListener("click", function() {
    ele.style.width = "1px";
    progress = 0;
    requestAnimationFrame(step);
}, false);

 

  

 

 

  

  运行效果如下:

  技术分享

 

二,深入requestAnimationFrame

  我们对动画的要求苛刻吗?

  对于时间要求苛刻的动画,永远不要使用 setTimeout() 或 setInterval()。

  时间间隔设置为 1000/60 毫秒,这相当于大约每秒 60 帧。这个数字是我对最佳帧速率的最佳估值,它可能不是一个很好的值,但是,因为 setInterval() 和 setTimeout() 完全不了解动画,所以由我指定帧速率。

浏览器肯定比我更了解何时绘制下一个动画帧,因此,如果改为由浏览器指定帧速率,会产生更好的结果。

  使用 setTimeout 和 setInterval() 甚至有一个更严重的缺陷。虽然您传递以毫秒为单位指定的这些方法的时间间隔,但这些方法没有精确到毫秒。事实上,根据 HTML 规范,

这些方法(为了节约资源)慷慨地拉长您指定的时间间隔。

 

  使用 setTimeout 和 setInterval() 甚至有一个更严重的缺陷。虽然您传递以毫秒为单位指定的这些方法的时间间隔,但这些方法没有精确到毫秒;事实上,根据 HTML 规范,这些方法(为了节约资源)慷慨地拉长您指定的时间间隔。

 

为了避免这些缺陷,对于时间要求苛刻的动画,不应使用 setTimeout() 和 setInterval(),而是应该使用 ”requestAnimationFrame()“

  我们要做两件实际的事:

  (1),兼容处理,不同浏览器厂商肯定内部实现有区别

 

window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame    ||
	function( callback ){
	window.setTimeout(callback, 1000 / 60);
	};
})();

  

  (2),另一种方式实现

  

(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);
        };
}());

  

三,相关分享

  (1),Tween.js 

  1 var Tween = {
  2     Linear: function(t, b, c, d) { return c*t/d + b; },
  3     Quad: {
  4         easeIn: function(t, b, c, d) {
  5             return c * (t /= d) * t + b;
  6         },
  7         easeOut: function(t, b, c, d) {
  8             return -c *(t /= d)*(t-2) + b;
  9         },
 10         easeInOut: function(t, b, c, d) {
 11             if ((t /= d / 2) < 1) return c / 2 * t * t + b;
 12             return -c / 2 * ((--t) * (t-2) - 1) + b;
 13         }
 14     },
 15     Cubic: {
 16         easeIn: function(t, b, c, d) {
 17             return c * (t /= d) * t * t + b;
 18         },
 19         easeOut: function(t, b, c, d) {
 20             return c * ((t = t/d - 1) * t * t + 1) + b;
 21         },
 22         easeInOut: function(t, b, c, d) {
 23             if ((t /= d / 2) < 1) return c / 2 * t * t*t + b;
 24             return c / 2*((t -= 2) * t * t + 2) + b;
 25         }
 26     },
 27     Quart: {
 28         easeIn: function(t, b, c, d) {
 29             return c * (t /= d) * t * t*t + b;
 30         },
 31         easeOut: function(t, b, c, d) {
 32             return -c * ((t = t/d - 1) * t * t*t - 1) + b;
 33         },
 34         easeInOut: function(t, b, c, d) {
 35             if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
 36             return -c / 2 * ((t -= 2) * t * t*t - 2) + b;
 37         }
 38     },
 39     Quint: {
 40         easeIn: function(t, b, c, d) {
 41             return c * (t /= d) * t * t * t * t + b;
 42         },
 43         easeOut: function(t, b, c, d) {
 44             return c * ((t = t/d - 1) * t * t * t * t + 1) + b;
 45         },
 46         easeInOut: function(t, b, c, d) {
 47             if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
 48             return c / 2*((t -= 2) * t * t * t * t + 2) + b;
 49         }
 50     },
 51     Sine: {
 52         easeIn: function(t, b, c, d) {
 53             return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
 54         },
 55         easeOut: function(t, b, c, d) {
 56             return c * Math.sin(t/d * (Math.PI/2)) + b;
 57         },
 58         easeInOut: function(t, b, c, d) {
 59             return -c / 2 * (Math.cos(Math.PI * t/d) - 1) + b;
 60         }
 61     },
 62     Expo: {
 63         easeIn: function(t, b, c, d) {
 64             return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
 65         },
 66         easeOut: function(t, b, c, d) {
 67             return (t==d) ? b + c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
 68         },
 69         easeInOut: function(t, b, c, d) {
 70             if (t==0) return b;
 71             if (t==d) return b+c;
 72             if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
 73             return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
 74         }
 75     },
 76     Circ: {
 77         easeIn: function(t, b, c, d) {
 78             return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
 79         },
 80         easeOut: function(t, b, c, d) {
 81             return c * Math.sqrt(1 - (t = t/d - 1) * t) + b;
 82         },
 83         easeInOut: function(t, b, c, d) {
 84             if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
 85             return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
 86         }
 87     },
 88     Elastic: {
 89         easeIn: function(t, b, c, d, a, p) {
 90             var s;
 91             if (t==0) return b;
 92             if ((t /= d) == 1) return b + c;
 93             if (typeof p == "undefined") p = d * .3;
 94             if (!a || a < Math.abs(c)) {
 95                 s = p / 4;
 96                 a = c;
 97             } else {
 98                 s = p / (2 * Math.PI) * Math.asin(c / a);
 99             }
100             return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
101         },
102         easeOut: function(t, b, c, d, a, p) {
103             var s;
104             if (t==0) return b;
105             if ((t /= d) == 1) return b + c;
106             if (typeof p == "undefined") p = d * .3;
107             if (!a || a < Math.abs(c)) {
108                 a = c; 
109                 s = p / 4;
110             } else {
111                 s = p/(2*Math.PI) * Math.asin(c/a);
112             }
113             return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
114         },
115         easeInOut: function(t, b, c, d, a, p) {
116             var s;
117             if (t==0) return b;
118             if ((t /= d / 2) == 2) return b+c;
119             if (typeof p == "undefined") p = d * (.3 * 1.5);
120             if (!a || a < Math.abs(c)) {
121                 a = c; 
122                 s = p / 4;
123             } else {
124                 s = p / (2  *Math.PI) * Math.asin(c / a);
125             }
126             if (t < 1) return -.5 * (a * Math.pow(2, 10* (t -=1 )) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
127             return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p ) * .5 + c + b;
128         }
129     },
130     Back: {
131         easeIn: function(t, b, c, d, s) {
132             if (typeof s == "undefined") s = 1.70158;
133             return c * (t /= d) * t * ((s + 1) * t - s) + b;
134         },
135         easeOut: function(t, b, c, d, s) {
136             if (typeof s == "undefined") s = 1.70158;
137             return c * ((t = t/d - 1) * t * ((s + 1) * t + s) + 1) + b;
138         },
139         easeInOut: function(t, b, c, d, s) {
140             if (typeof s == "undefined") s = 1.70158; 
141             if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
142             return c / 2*((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
143         }
144     },
145     Bounce: {
146         easeIn: function(t, b, c, d) {
147             return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b;
148         },
149         easeOut: function(t, b, c, d) {
150             if ((t /= d) < (1 / 2.75)) {
151                 return c * (7.5625 * t * t) + b;
152             } else if (t < (2 / 2.75)) {
153                 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
154             } else if (t < (2.5 / 2.75)) {
155                 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
156             } else {
157                 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
158             }
159         },
160         easeInOut: function(t, b, c, d) {
161             if (t < d / 2) {
162                 return Tween.Bounce.easeIn(t * 2, 0, c, d) * .5 + b;
163             } else {
164                 return Tween.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
165             }
166         }
167     }
168 }
169 Math.tween = Tween;

 

  (2),ball.js

 1 var ball = $("ball"), shadow = $("shadow");
 2 var objBall = {};
 3 var shadowWithBall = function(top) {
 4     // 0 ~ 500
 5     var top = top || parseInt(ball.css("top")),
 6     scale = 1 + (500 - top) / 300;
 7     opacity = 1 - (500 - top) / 300;
 8     if (opacity < 0) opacity = 0;
 9     shadow.css("opacity", opacity)
10     .css("WebkitTransform", "scale("+ [scale, scale].join(",") +")")
11     .css("transform", "scale("+ [scale, scale].join(",") +")");
12 }, funFall = function() {
13     var start = 0, during = 100;
14     var _run = function() {
15         start++;
16         var top = Tween.Bounce.easeOut(start, objBall.top, 500 - objBall.top, during);
17         ball.css("top", top);
18         shadowWithBall(top);
19         if (start < during) requestAnimationFrame(_run);
20     };
21     _run();
22 };
23 ball.bind("mousedown", function(event) {
24     objBall.pageY = event.pageY;
25     objBall.flagFollow = true;    
26 });
27 $(document).bind("mousemove", function(event) {
28     if (objBall.flagFollow) {
29         var pageY = event.pageY;
30         objBall.top = 500 - (objBall.pageY - pageY);
31         if (objBall.top < 0) {
32             objBall.top = 0;
33         } else if (objBall.top > 500) {
34             objBall.top = 500;
35         }
36         //cosnole.log(objBall.top);
37         ball.css("top", objBall.top);
38         shadowWithBall(objBall.top);    
39     }
40 });
41 $(document).bind("mouseup", function(event) {
42     if (objBall.flagFollow) funFall();
43     objBall.flagFollow = false;    
44 });

 

  效果如下:  

    技术分享

 

 

 

(四),最后总结

  (1),理解requestAnimationFrame Api的使用方式以及具体实例中使用的目的是为了解决哪些问题。

  (2),requestAnimationFrame 与 之前“setTimeout setInterval”的不同之处在哪。

  (3),熟练使用requestAnimationFrame对象,不断实践与重构文章中的栗子。

 

 

                   哈哈哈,本篇结束,未完待续,希望和大家多多交流够沟通,共同进步。。。。。。呼呼呼……(*^__^*)    

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。