大熊君学习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()”方法。

    大熊君学习html5系列之------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);

  

  

  运行效果如下:

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

二,深入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 

 var Tween = {
     Linear: function(t, b, c, d) { return c*t/d + b; },
     Quad: {
         easeIn: function(t, b, c, d) {
             return c * (t /= d) * t + b;
         },
         easeOut: function(t, b, c, d) {
             return -c *(t /= d)*(t-2) + b;
         },
         easeInOut: function(t, b, c, d) {
             if ((t /= d / 2) < 1) return c / 2 * t * t + b;
             return -c / 2 * ((--t) * (t-2) - 1) + b;
         }
     },
     Cubic: {
         easeIn: function(t, b, c, d) {
             return c * (t /= d) * t * t + b;
         },
         easeOut: function(t, b, c, d) {
             return c * ((t = t/d - 1) * t * t + 1) + b;
         },
         easeInOut: function(t, b, c, d) {
             if ((t /= d / 2) < 1) return c / 2 * t * t*t + b;
             return c / 2*((t -= 2) * t * t + 2) + b;
         }
     },
     Quart: {
         easeIn: function(t, b, c, d) {
             return c * (t /= d) * t * t*t + b;
         },
         easeOut: function(t, b, c, d) {
             return -c * ((t = t/d - 1) * t * t*t - 1) + b;
         },
         easeInOut: function(t, b, c, d) {
             if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
             return -c / 2 * ((t -= 2) * t * t*t - 2) + b;
         }
     },
     Quint: {
         easeIn: function(t, b, c, d) {
             return c * (t /= d) * t * t * t * t + b;
         },
         easeOut: function(t, b, c, d) {
             return c * ((t = t/d - 1) * t * t * t * t + 1) + b;
         },
         easeInOut: function(t, b, c, d) {
             if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
             return c / 2*((t -= 2) * t * t * t * t + 2) + b;
         }
     },
     Sine: {
         easeIn: function(t, b, c, d) {
             return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
         },
         easeOut: function(t, b, c, d) {
             return c * Math.sin(t/d * (Math.PI/2)) + b;
         },
         easeInOut: function(t, b, c, d) {
             return -c / 2 * (Math.cos(Math.PI * t/d) - 1) + b;
         }
     },
     Expo: {
         easeIn: function(t, b, c, d) {
             return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
         },
         easeOut: function(t, b, c, d) {
             return (t==d) ? b + c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
         },
         easeInOut: function(t, b, c, d) {
             if (t==0) return b;
             if (t==d) return b+c;
             if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
             return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
         }
     },
     Circ: {
         easeIn: function(t, b, c, d) {
             return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
         },
         easeOut: function(t, b, c, d) {
             return c * Math.sqrt(1 - (t = t/d - 1) * t) + b;
         },
         easeInOut: function(t, b, c, d) {
             if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
             return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
         }
     },
     Elastic: {
         easeIn: function(t, b, c, d, a, p) {
             var s;
             if (t==0) return b;
             if ((t /= d) == 1) return b + c;
             if (typeof p == "undefined") p = d * .3;
             if (!a || a < Math.abs(c)) {
                 s = p / 4;
                 a = c;
             } else {
                 s = p / (2 * Math.PI) * Math.asin(c / a);
             }
             return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
         },
         easeOut: function(t, b, c, d, a, p) {
             var s;
             if (t==0) return b;
             if ((t /= d) == 1) return b + c;
             if (typeof p == "undefined") p = d * .3;
             if (!a || a < Math.abs(c)) {
                 a = c;
                 s = p / 4;
             } else {
                 s = p/(2*Math.PI) * Math.asin(c/a);
             }
             return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
         },
         easeInOut: function(t, b, c, d, a, p) {
             var s;
             if (t==0) return b;
             if ((t /= d / 2) == 2) return b+c;
             if (typeof p == "undefined") p = d * (.3 * 1.5);
             if (!a || a < Math.abs(c)) {
                 a = c;
                 s = p / 4;
             } else {
                 s = p / (2  *Math.PI) * Math.asin(c / a);
             }
             if (t < 1) return -.5 * (a * Math.pow(2, 10* (t -=1 )) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
             return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p ) * .5 + c + b;
         }
     },
     Back: {
         easeIn: function(t, b, c, d, s) {
             if (typeof s == "undefined") s = 1.70158;
             return c * (t /= d) * t * ((s + 1) * t - s) + b;
         },
         easeOut: function(t, b, c, d, s) {
             if (typeof s == "undefined") s = 1.70158;
             return c * ((t = t/d - 1) * t * ((s + 1) * t + s) + 1) + b;
         },
         easeInOut: function(t, b, c, d, s) {
             if (typeof s == "undefined") s = 1.70158;
             if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
             return c / 2*((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
         }
     },
     Bounce: {
         easeIn: function(t, b, c, d) {
             return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b;
         },
         easeOut: function(t, b, c, d) {
             if ((t /= d) < (1 / 2.75)) {
                 return c * (7.5625 * t * t) + b;
             } else if (t < (2 / 2.75)) {
                 return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
             } else if (t < (2.5 / 2.75)) {
                 return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
             } else {
                 return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
             }
         },
         easeInOut: function(t, b, c, d) {
             if (t < d / 2) {
                 return Tween.Bounce.easeIn(t * 2, 0, c, d) * .5 + b;
             } else {
                 return Tween.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
             }
         }
     }
 }
 Math.tween = Tween;

  (2),ball.js

 var ball = $("ball"), shadow = $("shadow");
 var objBall = {};
 var shadowWithBall = function(top) {
     // 0 ~ 500
     var top = top || parseInt(ball.css("top")),
     scale = 1 + (500 - top) / 300;
     opacity = 1 - (500 - top) / 300;
     if (opacity < 0) opacity = 0;
     shadow.css("opacity", opacity)
     .css("WebkitTransform", "scale("+ [scale, scale].join(",") +")")
     .css("transform", "scale("+ [scale, scale].join(",") +")");
 }, funFall = function() {
     var start = 0, during = 100;
     var _run = function() {
         start++;
         var top = Tween.Bounce.easeOut(start, objBall.top, 500 - objBall.top, during);
         ball.css("top", top);
         shadowWithBall(top);
         if (start < during) requestAnimationFrame(_run);
     };
     _run();
 };
 ball.bind("mousedown", function(event) {
     objBall.pageY = event.pageY;
     objBall.flagFollow = true;
 });
 $(document).bind("mousemove", function(event) {
     if (objBall.flagFollow) {
         var pageY = event.pageY;
         objBall.top = 500 - (objBall.pageY - pageY);
         if (objBall.top < 0) {
             objBall.top = 0;
         } else if (objBall.top > 500) {
             objBall.top = 500;
         }
         //cosnole.log(objBall.top);
         ball.css("top", objBall.top);
         shadowWithBall(objBall.top);
     }
 });
 $(document).bind("mouseup", function(event) {
     if (objBall.flagFollow) funFall();
     objBall.flagFollow = false;
 });

  效果如下:  

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

(四),最后总结

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

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

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

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

上一篇:yaf学习资料


下一篇:使用DatePickerDialog、TimePickerDialog