0

So I am a canvas newbie and I've written some code trying to create an animation. I want to make a ball move with a parabola equation, everything works fine except for the fact that instead of having an animation I basically get a parabola made of arcs.

Here is the piece of code that I use for the animation:

// a b c are calculated in another function and they are used to calculate the parabola, x1 and y1 are the coordinates of the ball
function drawball(a,b,c,x1,y1){
    canvas=document.getElementById("mycanvas");
    ctx=canvas.getContext("2d");
    ctx.beginPath();
    ctx.arc(x1,y1,25,0,2*Math.PI);
    ctx.stroke();
    //stop when i get to the final point (x3 is a const)
    if(x1<x3){
        y1=a*(x1*x1)+b*x1+c; //parabola formula
        x1++;
        window.requestAnimationFrame(drawball(a,b,c,x1,y1));
    }
}

In chrome's console I get this error:

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

Thank you for your help!

SaschaM78
  • 4,376
  • 4
  • 33
  • 42
ddev
  • 65
  • 1
  • 9

1 Answers1

1

You are attempting to pass the return value of the drawball function into the requestAnimationFrame callback (which is undefined).

Here is an example in the chrome console when you try to pass a value to requestAnimationFrame that isn't a function

> requestAnimationFrame(console.log('hi'))

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

Versus passing an anonymous function:

> requestAnimationFrame(() => { console.log('hi') })

hi


You need to pass a function which will be called when the requestAnimationFrame timer elapses.

One example of this is:

// Vanilla Javascript
window.requestAnimationFrame(function() { drawball(a,b,c,x1,y1) } );

// ES6
window.requestAnimationFrame(() => { drawball(a,b,c,x1,y1) } );

In these cases the argument passed to requestAnimationFrame is a function which will be called and execute the drawball function.

tt9
  • 5,784
  • 6
  • 42
  • 65
  • Thank you for your quick answer! So the only thing that i need to do is to substitute my "requestAnimationFrame" with yours, am i correct? Could you please explain me why is this necessary? I just want to understand so that i can improve myself. – ddev Nov 09 '17 at 20:26
  • @Deci Like other functions requestAnimationFrame takes in an argument, but instead of it being a string or an int or boolean, it takes in an actual function, which can be ran by the parent function whenever it wants. And if you think about how it is working behind the scenes, requestAnimationFrame runs about once every 17 milliseconds, so it doesn't want to execute the drawball function immediately it wants to know what function it is supposed to run, wait 17 milliseconds and then run it.That is a simplified explanation, there is only so many characters in a comment, but checkout MDN for more. – tt9 Nov 09 '17 at 20:34
  • Thank you very much for your patience. Now the animation works very well. There's only one problem with it now: it leaves a trail of circles behind. I've tried to create a white cirlcle in the position of x1 and y1 in order to delete the ones left behind but the result is a bunch of gray circles. So how do i delete the old ones in order to have only one moving circle? – ddev Nov 09 '17 at 20:47
  • @Deci So it's a bit complicated to explain, and not really meant for the comments section here, but here is a [link to a good SO post](https://stackoverflow.com/questions/2142535/how-to-clear-the-canvas-for-redrawing) regarding your problem. The base idea is to clear the entire canvas each frame of animation. Once you do that you'll notice a flicker, you will need to implement double buffering. [See this link](https://stackoverflow.com/questions/2795269/does-html5-canvas-support-double-buffering) – tt9 Nov 09 '17 at 20:54