canvas requestAnimationFrame

不知道大家是否有这样的感觉,当你使用window.setInterval()或者window.setTimeout()制作出来的动画,效果总是一卡一卡的,这绝对不是大家想看到的。

两个函数的特征决定了它能展示给我们的效果: 
(1)其实它们从一生下来,就不是为动画服务的; 
(2)它们虽然可以以毫秒作为单位,但是它们永远达不到毫秒的精确性; 
(3)它们是死的,并不会考虑动画什么时间绘制才是最佳的,它们比较专一,脑子中只有你给予的那个数字(时间参数)。

所以大家在想,有没有这样一个方法:它们可以根据浏览器的性能自己决定绘制动画的速率。

也许在将来伟大的程序员会将window.setInterval()和window.setTimeout()的孙子打造出这样的性能,但是就现在来说,这个需求完全可以使用requestAnimationFrame()来完成。

requestAnimationFrame()接受一个参数,这个参数就是你要的动画引用(函数名)——为你省去了调试时间参数的烦恼。 
通过递归的方式,来完成动画的效果。下面来说说兼容性的问题:

(1)Firefox浏览器对requestAnimationFrame()的实现: 
Firefox浏览器在4.0版本中首次提供了该方法的变种——mozRequestAnimationFrame(),使用方法上是跟requestAnimationFrame()一致的。 
window.mozRequestAnimationFrame()在4.0版本中存在一个bug,就是动画的帧速率只能达到每秒30~40帧。如果要在Firefox4.0中应用动画,不要用mozRequestAnimationFrame()。

(2)Chorme浏览器对requestAnimationFrame()的实现; 
Chrome同样提供了动画实现函数——window.webkitRequestAnimationFrame()。Chrome10中也存在一个bug,就是浏览器没有将绘制的时间传递给动画的回调函数。所以导致回调函数中的time变量的值变成了undefined。在这种情况下,可以使用下面的表达式赋值time: 
time = +new Date();

(3)Internet Explorer浏览器对requestAnimationFrame()的实现: 
从IE10开始,提供了和W3C标准想似的方法——window.msRequestAnimationFrame()。

下面给大家写一个能兼容浏览器的requestAnimationFrame():

window.requestNextAnimationFrame = (function(){
        var originalWebkitMethod,
            wrapper = undefined,
            callback = undefined,
            geckoVersion = 0,
            userAgent = navigator.userAgent,
            index = 0,
            self = this;
        if(window.webkitRequestAnimationFrame){
            wrapper = function(time){
                if(time === undefined){
                    time += new Date();
                }
                self.callback(time);
            };
            originalWebkitMethod = window.webkitRequestAnimationFrame;
            window.webkitRequestAnimationFrame = function(callback,element){
                self.callback = callback;
                originalWebkitMethod(wrapper , element);
            }
        }
        if(window.mozRequestAnimationFrame){
            index = userAgent.indexOf('rv:');
            if(userAgent.indexOf('Gecko') != -1){
                geckoVersion = userAgent.substr(index+3 , 3);
                if(geckoVersion === '2.0'){
                    window.mozRequestAnimationFrame = undefined;
                }
            }
        }


        return window.requestNextAnimationFrame ||
               window.webkitRequestAnimationFrame ||
               window.mozRequestAnimationFrame ||
               window.oRequestAnimationFrame ||
               window.msRequestAnimationFrame ||


               function (callback , element){
                    var start,
                        finish;
                    window.setTimeout(function(){
                        start = +new Date();
                        callback(start);
                        finish = +new Date();
                        self.timeout = 1000/60 - (finish - start);
                    } , self.timeout);
               };
    })();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

只要将上面的动画的引用传入到方法,例如:

function animate(){
    ...
    requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

最后,如果要取消动画还有 
cancelRequestAnimationFrame()、 
webkitCancelRequestAnimationFrame()、 
msCancelRequestAnimationFrame()、 
mozCancelRequestAnimationFrame()

说了那么多废话,一定觉得不实际。上案例,让亲们更好地体验函数的强大: 
html

   <head>
     <title>Using requestAnimationFrame()</title>

      <style> 
         body {
            background: #dddddd;
         }

         #canvas {
            background: #ffffff;
            cursor: pointer;
            margin-left: 10px;
            margin-top: 10px;
            -webkit-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
            -moz-box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
            box-shadow: 3px 3px 6px rgba(0,0,0,0.5);
         }

         #controls {
            margin-top: 10px;
            margin-left: 15px;
         }
      </style>
   </head>

   <body>
      <div id='controls'>
         <input id='animateButton' type='button' value='Animate'/>
      </div>

      <canvas id='canvas' width='750' height='500'>
         Canvas not supported
      </canvas>

      <script src='../../shared/js/requestNextAnimationFrame.js'></script>
      <script src='example.js'></script>
   </body>
</html>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

js

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    paused = true,
    discs = [
      { 
         x: 150,
         y: 250,
         lastX: 150,
         lastY: 250,
         velocityX: -3.2,
         velocityY: 3.5,
         radius: 25,
         innerColor: 'rgba(255,255,0,1)',
         middleColor: 'rgba(255,255,0,0.7)',
         outerColor: 'rgba(255,255,0,0.5)',
         strokeStyle: 'gray',
      },

      { 
         x: 50,
         y: 150,
         lastX: 50,
         lastY: 150,
         velocityX: 2.2,
         velocityY: 2.5,
         radius: 25,
         innerColor: 'rgba(100,145,230,1.0)',
         middleColor: 'rgba(100,145,230,0.7)',
         outerColor: 'rgba(100,145,230,0.5)',
         strokeStyle: 'blue'
      },

      { 
         x: 150,
         y: 75,
         lastX: 150,
         lastY: 75,
         velocityX: 1.2,
         velocityY: 1.5,
         radius: 25,
         innerColor: 'rgba(255,0,0,1.0)',
         middleColor: 'rgba(255,0,0,0.7)',
         outerColor: 'rgba(255,0,0,0.5)',
         strokeStyle: 'orange'
      },
   ],
   numDiscs = discs.length,
   animateButton = document.getElementById('animateButton');

// Functions.....................................................

function drawBackground() {
   var STEP_Y = 12,
       i = context.canvas.height;

   context.strokeStyle = 'lightgray';
   context.lineWidth = 0.5;

   while(i > STEP_Y*4) {
      context.beginPath();
      context.moveTo(0, i);
      context.lineTo(context.canvas.width, i);
      context.stroke();
      i -= STEP_Y;
   }

   context.save();

   context.strokeStyle = 'rgba(100,0,0,0.3)';
   context.lineWidth = 1;

   context.beginPath();

   context.moveTo(35,0);
   context.lineTo(35,context.canvas.height);
   context.stroke();

   context.restore();
}

function update() {
   var disc = null;

   for(var i=0; i < numDiscs; ++i) {
      disc = discs[i];

      if (disc.x + disc.velocityX + disc.radius > context.canvas.width ||
          disc.x + disc.velocityX - disc.radius < 0) 
         disc.velocityX = -disc.velocityX;

      if (disc.y + disc.velocityY + disc.radius > context.canvas.height ||
          disc.y + disc.velocityY - disc.radius  < 0) 
         disc.velocityY= -disc.velocityY;

      disc.x += disc.velocityX;
      disc.y += disc.velocityY;
   }
}

function draw() {
   var disc = discs[i];

   for(var i=0; i < numDiscs; ++i) {
      disc = discs[i];

      gradient = context.createRadialGradient(disc.x, disc.y, 0,
                         disc.x, disc.y, disc.radius);

      gradient.addColorStop(0.3, disc.innerColor);
      gradient.addColorStop(0.5, disc.middleColor);
      gradient.addColorStop(1.0, disc.outerColor);

      context.save();
      context.beginPath();
      context.arc(disc.x, disc.y, disc.radius, 0, Math.PI*2, false);
      context.fillStyle = gradient;
      context.strokeStyle = disc.strokeStyle;
      context.fill();
      context.stroke();
      context.restore();
   }
}

// Animation.....................................................

function animate(time) {
   if (!paused) {
      context.clearRect(0,0,canvas.width,canvas.height);
      drawBackground();
      update();
      draw();

      window.requestNextAnimationFrame(animate);
   }
}

// Initialization................................................

context.font = '48px Helvetica';

animateButton.onclick = function (e) {
   paused = paused ? false : true;
   if (paused) {
      animateButton.value = 'Animate';
   }
   else {
     window.requestNextAnimationFrame(animate);
      animateButton.value = 'Pause';
   }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值