0

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 ?

1 Answer 1

2

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>
Sign up to request clarification or add additional context in comments.

1 Comment

Can you please tell me what other problem these codes have please ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.