首页 > 解决方案 > Javascript - 暂停和继续 requestAnimationFrame 循环

问题描述

我正在尝试requestAnimationFrame使用递归函数在两个方向上为图像在画布上的移动设置动画。

Animate 1单击 1 次将图像向一个方向移动,然后单击 2 次向相反方向移动。Animate 2向一个方向移动图像。

在我开始测试我的递归函数之前,我requestAnimationFrame在第一次点击Animate 1. 我运行了一个简单的调试,发现在我第一次单击后Animate 1,图像成功地向左移动。但是当它退出时animation2(),它返回requestAnimationFrame到返回以下错误:

Uncaught TypeError: Failed to execute 'requestAnimationFrame' on 'Window': The callback provided as parameter 1 is not a function.

是什么导致了这个错误?而且我很确定我的递归函数不会按原样工作。我应该做哪些更正?

以下是我的粗略编码。

var canvas = document.getElementsByTagName("canvas")[0];
canvas.width = 286;
canvas.height = 60;
var ctx = canvas.getContext("2d");
trackTransforms(ctx);

var img = new Image();
img.src = "https://www.dropbox.com/s/bq8wk2rashbsjxw/Capture.PNG?raw=1";

window.onload = function() {
  redraw();
};

var button1 = false;
var pause;
var coordinate = new Array();
coordinate[0] = {
  X1: 286,
  X2: 136
};
coordinate[1] = {
  X1: 150,
  X2: 0
};
var coordinate2 = new Array();
coordinate2[0] = {
  X1: 286,
  X2: 136
};
coordinate2[1] = {
  X1: 150,
  X2: 0
};

function animation() {
  var track = coordinate.slice(); // copy the array to keep track of which items are left to process after click event
  coordinate.forEach(function(item, index) {
    track.splice(track.indexOf(item), 1); // remove the item from the processed array
    button1 = true;
    animation2(item);
    button1 = false;

    button.onclick = function(event) {
      animation(track); // call function in the callback passing in the unprocessed items array
      event.target.onclick = null; // remove the click event so it can't be fired again
    }

  });
}

var fps = 1; // animation speed
var slideX = 0;

function animation2(coordinate) {
  if (button1) {
    var cornerRight = coordinate.X1; // animation in to and fro
    var cornerLeft = coordinate.X2;
  } else {
    var cornerRight = coordinate2[0].X1; // animation in single direction
    var cornerLeft = coordinate2[0].X2; // change index number to change direction
  }

  var cornerCenterX = (cornerLeft + cornerRight) / 2;
  var canvasCenterX = canvas.width / 2;

  if (cornerCenterX > canvasCenterX) {
    // to determine image to move right or left
    slideX -= fps;
    var distanceX = Math.abs(cornerCenterX - canvasCenterX);
    var pt = ctx.transformedPoint(slideX, 0);
    ctx.translate(pt.x, pt.y);
    redraw();
    if (button1) {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2(coordinate));
    } else {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2);
    }

  } else if (cornerCenterX < canvasCenterX) {
    slideX += fps;
    var distanceX = Math.abs(cornerCenterX - canvasCenterX);
    var pt = ctx.transformedPoint(slideX, 0);
    ctx.translate(pt.x, pt.y);
    redraw();
    if (button1) {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2(coordinate));
    } else {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2);
    }
  }
}

function redraw() {
  var p1 = ctx.transformedPoint(0, 0);
  var p2 = ctx.transformedPoint(canvas.width, canvas.height);
  ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
  ctx.save();
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.restore();
  ctx.drawImage(img, 0, 0);
}

function reset() {
  var p1 = ctx.transformedPoint(0, 0);
  var p2 = ctx.transformedPoint(canvas.width, canvas.height);
  ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(img, 0, 0);
  slideX = 0;
  button1 = false;
}

function trackTransforms(ctx) {
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  var xform = svg.createSVGMatrix();
  ctx.getTransform = function() {
    return xform;
  };

  var savedTransforms = [];
  var save = ctx.save;
  ctx.save = function() {
    savedTransforms.push(xform.translate(0, 0));
    return save.call(ctx);
  };

  var restore = ctx.restore;
  ctx.restore = function() {
    xform = savedTransforms.pop();
    return restore.call(ctx);
  };

  var translate = ctx.translate;
  ctx.translate = function(dx, dy) {
    xform = xform.translate(dx, dy);
    return translate.call(ctx, dx, dy);
  };

  var transform = ctx.transform;
  ctx.transform = function(a, b, c, d, e, f) {
    var m2 = svg.createSVGMatrix();
    m2.a = a;
    m2.b = b;
    m2.c = c;
    m2.d = d;
    m2.e = e;
    m2.f = f;
    xform = xform.multiply(m2);
    return transform.call(ctx, a, b, c, d, e, f);
  };

  var setTransform = ctx.setTransform;
  ctx.setTransform = function(a, b, c, d, e, f) {
    xform.a = a;
    xform.b = b;
    xform.c = c;
    xform.d = d;
    xform.e = e;
    xform.f = f;
    return setTransform.call(ctx, a, b, c, d, e, f);
  };

  // convert the mouse coordinates (in pixels) into the global space of your SVG document
  var pt = svg.createSVGPoint();
  ctx.transformedPoint = function(x, y) {
    pt.x = x;
    pt.y = y;
    return pt.matrixTransform(xform.inverse());
  };
}
body {
  background: #eee;
  margin: 1em;
  text-align: center;
}

canvas {
  display: block;
  margin: 1em auto;
  background: #fff;
  border: 1px solid #ccc;
}
<input id="button" type="button" value="Animate 1" onclick="animation();" />
<input id="button2" type="button" value="Animate 2" onclick="animation2();" />
<input id="button3" type="button" value="Reset" onclick="reset();" />
<canvas id="main"></canvas>

标签: javascripthtmlhtml5-canvas

解决方案


跟进评论:

您应该阅读有关requestAnimationFrame方法的更多信息:
https ://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

语法是:window.requestAnimationFrame(callback);
callback是一个参数,指定在为下一次重绘更新动画时要调用的函数。

以您的代码为例:

  • animation2是一个函数
  • animation2(coordinate)不是函数
  • function() { animation2(coordinate) }是一个函数

考虑阅读无名/匿名函数



这是您的代码没有错误地工作:

var canvas = document.getElementsByTagName("canvas")[0];
canvas.width = 286;
canvas.height = 60;
var ctx = canvas.getContext("2d");
trackTransforms(ctx);

var img = new Image();
img.src = "https://www.dropbox.com/s/bq8wk2rashbsjxw/Capture.PNG?raw=1";

window.onload = function() {
  redraw();
};

var button1 = false;
var pause;
var coordinate = new Array();
coordinate[0] = {
  X1: 286,
  X2: 136
};
coordinate[1] = {
  X1: 150,
  X2: 0
};
var coordinate2 = new Array();
coordinate2[0] = {
  X1: 286,
  X2: 136
};
coordinate2[1] = {
  X1: 150,
  X2: 0
};

function animation() {
  var track = coordinate.slice(); // copy the array to keep track of which items are left to process after click event
  coordinate.forEach(function(item, index) {
    track.splice(track.indexOf(item), 1); // remove the item from the processed array
    button1 = true;
    animation2(item);
    button1 = false;

    button.onclick = function(event) {
      animation(track); // call function in the callback passing in the unprocessed items array
      event.target.onclick = null; // remove the click event so it can't be fired again
    }

  });
}

var fps = 1; // animation speed
var slideX = 0;

function animation2(coordinate) {
  if (button1) {
    var cornerRight = coordinate.X1; // animation in to and fro
    var cornerLeft = coordinate.X2;
  } else {
    var cornerRight = coordinate2[0].X1; // animation in single direction
    var cornerLeft = coordinate2[0].X2; // change index number to change direction
  }

  var cornerCenterX = (cornerLeft + cornerRight) / 2;
  var canvasCenterX = canvas.width / 2;

  if (cornerCenterX > canvasCenterX) {
    // to determine image to move right or left
    slideX -= fps;
    var distanceX = Math.abs(cornerCenterX - canvasCenterX);
    var pt = ctx.transformedPoint(slideX, 0);
    ctx.translate(pt.x, pt.y);
    redraw();
    if (button1) {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(function() { animation2(coordinate) });
    } else {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2);
    }

  } else if (cornerCenterX < canvasCenterX) {
    slideX += fps;
    var distanceX = Math.abs(cornerCenterX - canvasCenterX);
    var pt = ctx.transformedPoint(slideX, 0);
    ctx.translate(pt.x, pt.y);
    redraw();
    if (button1) {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(function() { animation2(coordinate) });
    } else {
      if (Math.abs(slideX) < distanceX) requestAnimationFrame(animation2);
    }
  }
}

function redraw() {
  var p1 = ctx.transformedPoint(0, 0);
  var p2 = ctx.transformedPoint(canvas.width, canvas.height);
  ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
  ctx.save();
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.restore();
  ctx.drawImage(img, 0, 0);
}

function reset() {
  var p1 = ctx.transformedPoint(0, 0);
  var p2 = ctx.transformedPoint(canvas.width, canvas.height);
  ctx.clearRect(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(img, 0, 0);
  slideX = 0;
  button1 = false;
}

function trackTransforms(ctx) {
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  var xform = svg.createSVGMatrix();
  ctx.getTransform = function() {
    return xform;
  };

  var savedTransforms = [];
  var save = ctx.save;
  ctx.save = function() {
    savedTransforms.push(xform.translate(0, 0));
    return save.call(ctx);
  };

  var restore = ctx.restore;
  ctx.restore = function() {
    xform = savedTransforms.pop();
    return restore.call(ctx);
  };

  var translate = ctx.translate;
  ctx.translate = function(dx, dy) {
    xform = xform.translate(dx, dy);
    return translate.call(ctx, dx, dy);
  };

  var transform = ctx.transform;
  ctx.transform = function(a, b, c, d, e, f) {
    var m2 = svg.createSVGMatrix();
    m2.a = a;
    m2.b = b;
    m2.c = c;
    m2.d = d;
    m2.e = e;
    m2.f = f;
    xform = xform.multiply(m2);
    return transform.call(ctx, a, b, c, d, e, f);
  };

  var setTransform = ctx.setTransform;
  ctx.setTransform = function(a, b, c, d, e, f) {
    xform.a = a;
    xform.b = b;
    xform.c = c;
    xform.d = d;
    xform.e = e;
    xform.f = f;
    return setTransform.call(ctx, a, b, c, d, e, f);
  };

  // convert the mouse coordinates (in pixels) into the global space of your SVG document
  var pt = svg.createSVGPoint();
  ctx.transformedPoint = function(x, y) {
    pt.x = x;
    pt.y = y;
    return pt.matrixTransform(xform.inverse());
  };
}
body {
  background: #eee;
  margin: 1em;
  text-align: center;
}

canvas {
  display: block;
  margin: 1em auto;
  background: #fff;
  border: 1px solid #ccc;
}
<input id="button" type="button" value="Animate 1" onclick="animation();" />
<input id="button2" type="button" value="Animate 2" onclick="animation2();" />
<input id="button3" type="button" value="Reset" onclick="reset();" />
<canvas id="main"></canvas>


推荐阅读