如何使用 requestAnimationFrame 为循环中的多个方块设置动画

How to use requestAnimationFrame to animate multiple squares in a loop

我正在使用 HTML canvas 绘制多个正方形。我有 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);

你可以这样做

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 正在使用的垫片,以便将当前时间戳传递给 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());
        };
    })();

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

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);
<canvas id="canvas" width="600" height="600"></canvas>