【问题标题】:How to use requestAnimationFrame to animate multiple squares in a loop如何使用 requestAnimationFrame 在循环中为多个正方形设置动画
【发布时间】:2016-10-04 14:34:38
【问题描述】:

我正在使用 HTML 画布绘制多个正方形。我有 2 个功能:1)绘制一个正方形和 2)在一个循环内绘制多个正方形。

现在我想使用 requestAnimationFrame 为这些正方形设置动画,一次绘制一个。我怎样才能做到这一点。这是jsFiddle

var canvas = document.getElementById('canvas'),
       ctx = canvas.getContext('2d');

    function rect(x, y, w, h) {
      ctx.beginPath();
      ctx.rect(x, y, w, h);
      ctx.stroke();
    }

    function drawRect(number, size) {
      for (var i = 0; i <= number; i++) {
        rect(i * size, i * size, (i * size) * 2, (i * size) * 2);
      }
    }

    drawRect(10, 5);

【问题讨论】:

    标签: javascript canvas requestanimationframe


    【解决方案1】:

    你可以这样做

    var numRects = 10;
    var size = 5;
    var i = 1;                          // which rectangle we're drawing
    var delay = 1000/60;                // num miliseconds between frames
    var before = new Date().getTime(),  // last draw time in ms
        now;                            // current time in ms
    
    function animateRect() {
      // get the current time to find if we should draw
      now = new Date().getTime();
    
      // if sufficient time passed since last draw, draw a rect
      if ( now - before > delay && i <= numRects) {
        rect(i * size, i * size, (i * size) * 2, (i * size) * 2);
        i++;
        before = now;
      }
    
      requestAnimFrame(animateRect);
    }
    

    编辑:

    正如 Blindman67 在下面指出的,requestAnimFrame 将自动画开始以来的当前时间戳传递给回调。以下是如何利用它:

    var numRects = 10;
    var size = 5;
    var i = 1;                          // which rectangle we're drawing
    var delay = 1000/60;                // num miliseconds between frames
    var before;                         // last draw time in ms
    
    function animateRect(now) {
        if ( !before ) before = now;
        // if sufficient time passed since last draw, draw a rect   
        if ( now - before > delay && i <= numRects) {
            rect(i * size, i * size, (i * size) * 2, (i * size) * 2);
            i++;
            before = now;
        }
    
        requestAnimFrame(animateRect);
    }
    

    但是,这需要修改 OP 正在使用的 shim,以便将当前时间戳传递给 setTimeout 中的回调:

    window.requestAnimFrame = (function() {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function( /* function */ callback, /* DOMElement */ element) {
                window.setTimeout(callback, 1000 / 60, new Date.now());
            };
        })();
    

    【讨论】:

    • 这正是我想要实现的。谢谢!
    • @kayee 我刚刚意识到我忘记在每次抽奖时更新before 抽奖时间。您必须将before = now; 行添加到条件主体。有关详细信息,请参阅我的编辑。
    • 我只想指出 requestAnimationFrame 将参数 time 传递给回调。时间以毫秒为单位(与日期相同),某些浏览器甚至具有更高的精度,毫秒数,因此无需从日期对象中获取时间。
    • @Blindman67 我根据您的反馈编辑了答案。感谢您指出这一点!
    【解决方案2】:

    我提供了一个帧限制器和补间来向您展示不同的动画方式。帧限制器具有您示例中的步骤,而补间具有在给定时间内完成所需的尽可能多的步骤。

    var canvas = document.getElementById('canvas'),
      ctx = canvas.getContext('2d');
    
    //requestAnim shim layer by Paul Irish
    //http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    window.requestAnimFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function( /* function */ callback, /* DOMElement */ element) {
          window.setTimeout(callback, 1000 / 60);
        };
    })();
    
    function rect(x, y, w, h, color) {
      ctx.beginPath();
      ctx.strokeStyle = color;
      ctx.rect(x, y, w, h);
      ctx.stroke();
    }
    
    function drawRect(i, size, color) {
      //for (var i = 0; i <= number; i++) {
      rect(i * size, i * size, (i * size) * 2, (i * size) * 2, color);
      //}
    }
    
    var i = 0;
    var incr = 1;
    var i_max = 10;
    var size = 5;
    var fps = 10;
    var delay = 1000 / fps;
    var lastFrame = 0;
    
    var animationTime = 5000
    var tweenStep = i_max / ((animationTime/1000) * 60);
    var j = 0;
    
    function animateRect() {
    
      // draw at 60fps
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      drawRect(i, size, "#0000FF");
      
      // This is a frame limiter.
      var currentFrame = Date.now();
      
      if ((currentFrame - lastFrame) >= delay) {
        i += incr;
        if (i >= i_max) i = i_max - 2, incr = -1;
        if (i < 0) i = 1, incr = 1;
        lastFrame = currentFrame;
      }
      
      // this is a tween. The step is calculated for the desired time.
      drawRect(j, size, "#FF0000");
      j += tweenStep;
      if (j >= i_max) tweenStep *= -1,j=i_max-1;
      if (j < 0) tweenStep *= -1, j=0;
      
    
      requestAnimFrame(animateRect);
      //draw rectangle one by one here...
    }
    
    animateRect();
    //drawRect(10, 5);
    &lt;canvas id="canvas" width="600" height="600"&gt;&lt;/canvas&gt;

    【讨论】:

    • 谢谢。这是完美的。
    猜你喜欢
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多