cancelAnimationFrame()
does not seem to work when called inside an object's method. I have tried binding the this
value to the callback function (as demonstrated on MDN with setTimeout
) but I received a TypeError when using cancelAnimationFrame()
. I then tried setting the this
value to a local variable called _this
and called cancelAnimationFrame()
again. That time, I did not receive an error but the animation itself is still playing. How do I cancel the animation?
I have recreated the issue I am having below. If you open a console window, you will see that the animation is still running.
function WhyWontItCancel() {
this.canvas = document.createElement("canvas");
this.canvas.width = 200;
this.canvas.height = 10;
document.body.appendChild(this.canvas);
this.draw = this.canvas.getContext("2d");
this.draw.fillStyle = "#f00";
this.position = 0;
};
WhyWontItCancel.prototype.play = function() {
if (this.position <= 190) {
this.draw.clearRect(0, 0, 400, 10);
this.draw.fillRect(this.position, 0, 10, 10);
this.position += 2;
} else {
//window.cancelAnimationFrame(this.animation.bind(this));
var _this = this;
window.cancelAnimationFrame(_this.animation);
console.log("still running");
}
this.animation = window.requestAnimationFrame(this.play.bind(this));
};
var animation = new WhyWontItCancel();
animation.play();
Seems that you miss two things here. First, this.animation = window.requestAnimationFrame(this.play.bind(this));
line is invoked always when play()
is called. Contrary to what you might think, cancelAnimationFrame
only removes the previously requested RAF call. Strictly speaking, it's not even necessary here. Second, you don't have to bind on each RAF call; you might do it just once:
function AnimatedCanvas() {
this.canvas = document.createElement("canvas");
this.canvas.width = 200;
this.canvas.height = 10;
document.body.appendChild(this.canvas);
this.draw = this.canvas.getContext("2d");
this.draw.fillStyle = "#f00";
this.position = 0;
this.play = this.play.bind(this); // takes `play` from prototype object
};
AnimatedCanvas.prototype.play = function() {
if (this.position <= 190) {
this.draw.clearRect(0, 0, 400, 10);
this.draw.fillRect(this.position, 0, 10, 10);
this.position += 2;
this.animationId = window.requestAnimationFrame(this.play);
}
};
You might want to add cancel into your prototype to be able to stop your animation, for example:
AnimatedCanvas.prototype.cancel = function() {
if (this.animationId) {
window.cancelAnimationFrame(this.animationId);
}
};
... but the point is, it's not useful in the use case described in the question.