T.PC
T.PC

Reputation: 61

Javascript - Pausing and continuing requestAnimationFrame loop

I'm trying to animate the movement of image across the canvas using requestAnimationFrame in 2 directions using a recursive function.

Animate 1 move the image in one direction on 1 click and then the opposite direction on 2nd click. Animate 2 moves the image in one direction.

Before I get to test my recursive function, my requestAnimationFrame returns an error on my first click of Animate 1. I ran a simple debug to find out that after my first click on Animate 1, the image animates successfully to the left. But as it exits the animation2(), it returns back to requestAnimationFrame to return the following error:

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

What causes this error? And I'm pretty sure my recursive function will not work as it is. What correction should I make?

The following is my crude coding.

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>

Upvotes: 1

Views: 1042

Answers (1)

Helder Sepulveda
Helder Sepulveda

Reputation: 17584

Following up from the comments:

You should read more about the requestAnimationFrame method:
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

The syntax is: window.requestAnimationFrame(callback);
That callback is a parameter specifying a function to call when it's time to update your animation for the next repaint.

Using your code as an example:

  • animation2 is a function
  • animation2(coordinate) is NOT a function
  • function() { animation2(coordinate) } is a function

Consider reading about nameless/anonymous functions



Here is your code working without errors:

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>

Upvotes: 1

Related Questions