美文网首页
canvas动画函数requestAnimationFrame

canvas动画函数requestAnimationFrame

作者: 年轻人多学点 | 来源:发表于2021-03-23 18:07 被阅读0次

    不知道大家是否有这样的感觉,当你使用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);
                   };
        })();
    

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

    function animate(){
        ...
        requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
    

    最后,如果要取消动画还有

    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>
    
    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';
       }
    };
    

    好了,这篇就到此结束,有什么问题欢迎亲们留言哦!

    相关文章

      网友评论

          本文标题:canvas动画函数requestAnimationFrame

          本文链接:https://www.haomeiwen.com/subject/mtafhltx.html