Reputation: 6269
I implemented a small script to test requestAnimationFrame and it generates "Uncaught RangeError: Maximum call stack size exceeded" error ..This is the code
<html>
<head>
<meta charset="utf-8">
<script>
window.onload=function(){
if (document.createElement("canvas").getContext){
//alert("browser supports canvas");
//console.log(document.getElementById("canvas").getContext);
canvas = document.getElementById("canvas");
shape = new shapes();
shape.drawball(canvas,5,"red");
}
};
function shapes(){
this.drawtriangle = function(canvas){
triangles = new triangle(0,0,0,200,200,200);
triangles.draw(canvas.getContext('2d'));
}
this.drawball = function(canvas,radius,color) {
ball = new Ball(radius,color);
ball.drawBall(canvas.getContext('2d'),canvas);
}
}
function coordinates(x1,y1){
this.x = x1;
this.y = y1;
}
function angle(angle){
this.angle = angle;
}
function Ball(radius,color){
this.origin = new coordinates(100,100);
this.radius = (radius === "undefined" ) ? 5 : radius;
this.color = (color === "undefined") ? red : color;
this.rotation = 0;
this.index = 0;
this.angles = new angle(0);
this.speed = 10;
}
Ball.prototype.drawBall = function (context,canvas){
context.fillStyle = this.color;
context.strokeStyle = "blue";
context.rotate(this.rotation);
context.beginPath();
context.arc(this.origin.x,this.origin.y,this.radius,0,(Math.PI*2),true);
context.closePath();
context.fill();
context.stroke(); this.animate(context,canvas);
}
Ball.prototype.animate = function (context,canvas){
var that = this;console.log(".......");
var time = new Date().getTime() * 0.002;
this.origin.x = Math.sin( time ) * 96 + 128;
this.origin.y = Math.cos( time * 0.9 ) * 96 + 128;
//context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + this.speed;
this.origin.y = this.origin.y + this.speed;
this.angles.angle = this.angles.angle + this.speed;
window.webkitrequestAnimationFrame(that.drawBall(context,canvas));
}
</script>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000px" height="1000px">
Your browser dows bot suppoet canvas
</canvas>
</body>
</html>
I got another code from the net and this works fine although it has recursion..
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>RequestAnimationFrame.js example</title>
</head>
<body>
<script >/**
* Provides requestAnimationFrame in a cross browser way.
* http://paulirish.com/2011/requestanimationframe-for-smart-animating/
*/
if ( !window.requestAnimationFrame ) {
window.requestAnimationFrame = ( function() {
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || // comment out if FF4 is slow (it caps framerate at ~30fps: https://bugzilla.mozilla.org/show_bug.cgi?id=630127)
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
window.setTimeout( callback, 1000 / 60 );
};
} )();
}</script>
<script>
var canvas, context;
init();
animate();
function init() {
canvas = document.createElement( 'canvas' );
canvas.width = 256;
canvas.height = 256;
context = canvas.getContext( '2d' );
document.body.appendChild( canvas );
}
function animate() {
requestAnimationFrame( animate );
draw();
}
function draw() {
var time = new Date().getTime() * 0.002;
var x = Math.sin( time ) * 96 + 128;
var y = Math.cos( time * 0.9 ) * 96 + 128;
context.fillStyle = 'rgb(245,245,245)';
context.fillRect( 0, 0, 255, 255 );
context.fillStyle = 'rgb(255,0,0)';
context.beginPath();
context.arc( x, y, 10, 0, Math.PI * 2, true );
context.closePath();
context.fill();
}
</script>
<div style="width:256px">
<a href="javascript:location='view-source:' + window.location.href;">view source</a><br /><br/>
<a href="http://dev.chromium.org/developers/design-documents/requestanimationframe-implementation">requestAnimationFrame()</a> allows modern browsers to stop drawing graphics when a tab or window is not visible. Improving overall performance and batteries on mobile devices.<br /><br />
<a href="https://gist.github.com/838785">RequestAnimationFrame.js</a> emulates the basic usage for old browsers.
</div>
</body>
</html>
What may be causing this error and how do I fix it ?
Upvotes: 0
Views: 3315
Reputation: 2650
Ball.animate
calls Ball.drawBall
and vice versa. So, before either finishes executing, they call each other until the "call stack" size exceeds its limit (causing the error).
Instead of
ball.drawBall(canvas.getContext('2d'),canvas);
try
setInterval(function () { ball.animate(canvas.getContext('2d'),canvas); }, 1000/60);
and remove
this.animate(context,canvas);
from Ball.prototype.drawBall
There are many, many problems with your code, but that's the one you asked about.
I've explained the simple errors. My annotations are in /* */
comments. There are also formatting and style errors, which I've omitted.
<html>
<head>
<meta charset="utf-8">
<script>
window.onload=function(){
if (document.createElement("canvas").getContext){
//alert("browser supports canvas");
//console.log(document.getElementById("canvas").getContext);
canvas = document.getElementById("canvas");
shape = new shapes();
/* drawball should be called by a run() function, or similar */
shape.drawball(canvas,5,"red");
}
};
/**
* Is this supposed to be a singleton, or perhaps a module?
* otherwise, you should use prototype to define these methods
*/
function shapes(){
this.drawtriangle = function(canvas){
/* triangles isn't defined in this file? */
triangles = new triangle(0,0,0,200,200,200);
/* I'd store a reference to context as a property of the function (class)
* aka a "static variable" */
triangles.draw(canvas.getContext('2d'));
}
this.drawball = function(canvas,radius,color) {
ball = new Ball(radius,color);
/* same here */
ball.drawBall(canvas.getContext('2d'),canvas);
}
}
/**
* this is reasonable, but don't pluralize a class name unless you mean it
* I'd maybe call it "Point" or "Vec2d"
*/
function coordinates(x1,y1){
this.x = x1;
this.y = y1;
}
/* so you'd use this object as angle.angle?? Just store it as a scalar.*/
function angle(angle){
this.angle = angle;
}
/* This is correct, I'm sure it's also copy/pasted */
function Ball(radius,color){
this.origin = new coordinates(100,100);
this.radius = (radius === "undefined" ) ? 5 : radius;
this.color = (color === "undefined") ? red : color;
this.rotation = 0;
this.index = 0;
this.angles = new angle(0);
this.speed = 10;
}
/**
* Ball.drawBall()? I'd use Ball.draw()
* I'd again store context in the function object, and you don't need to
* pass canvas
*/
Ball.prototype.drawBall = function (context,canvas){
context.fillStyle = this.color;
context.strokeStyle = "blue";
context.rotate(this.rotation);
context.beginPath();
context.arc(this.origin.x,this.origin.y,this.radius,0,(Math.PI*2),true);
context.closePath();
context.fill();
context.stroke();
/* There is no reason to have your whole animation call here,
* there should only be code to _draw_ the _ball_ (like the function says) */
this.animate(context,canvas);
}
/* This makes sense for a singleton Ball, but in this case animate should be
* on a containing object. The Ball doesn't animate anything, the App does */
Ball.prototype.animate = function (context,canvas){
/* I can't explain this. I'm 99.999% sure it's not what you want. */
var that = this;console.log(".......");
var time = new Date().getTime() * 0.002;
this.origin.x = Math.sin( time ) * 96 + 128;
this.origin.y = Math.cos( time * 0.9 ) * 96 + 128;
//context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + this.speed;
this.origin.y = this.origin.y + this.speed;
this.angles.angle = this.angles.angle + this.speed;
/* you complete your cycle here (animate calls drawBall and vice versa) */
window.webkitrequestAnimationFrame(that.drawBall(context,canvas));
}
</script>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000px" height="1000px">
Your browser dows bot suppoet canvas
</canvas>
</body>
</html>
Upvotes: 2