0

I'm trying to create a canvas animation with 2 objects: a circumference and a filled circle. My objective is to make it seem that the circumference represents the circles orbit. However when trying to animate there's no animation and only when I click to stop the page does the image appear with the circle in a random position in the orbit (this means that the moving part works). Thank you for your time and here's the code:

function restartAnimate(){
    runAnimation(0);
    setTimeout(restartAnimate(),1000);
}

function runAnimation(i){
    let animation = document.getElementById("Animation");
    let anim = animation.getContext("2d");
    anim.clearRect(0,0,300,150);
    anim.save();

    anim.strokeStyle = "#99ebff";
    anim.lineWidth = 10;
    anim.beginPath();
    anim.arc(150, 75, 40, 0, 2 * Math.PI);
    anim.stroke();

    anim.restore();
    anim.save()
    anim.fillStyle = "#000000";
    anim.translate(150,75);
    anim.rotate(2 * Math.PI * i / 1000);
    anim.translate(-150,-75);
    anim.beginPath();
    anim.arc(150 + 36.5, 75 ,13, 0, 2 * Math.PI);
    anim.fill();

    anim.restore();

    i += 16;
    if(i < 1000) setTimeout(runAnimation(i),16);
}
2
  • setTimeout(()=>runAnimation(i),16);
    – kockburn
    Commented Nov 26, 2018 at 19:15
  • 1
    Thanks! It was exactly that problem.
    – DeyL Xazo
    Commented Nov 26, 2018 at 19:19

1 Answer 1

1

You should use requestAnimationFrame to animate so that the render results are displayed in sync with the display hardware refresh.

setTimeout is very inaccurate and your function will fall behind over time. If you use requestAnimationFrame you can use the first argument (time in ms) to keep precisely on time.

ctx.save, and ctx.restore can be very expensive calls and should be avoided if you can. As you are only restoring the transform you can set it manually as needed with ctx.setTransform()

There is no need to restart the animation, just let it cycle.

Example rewrites your code with above points in mind and some other changes. See code comments for more info.

// Define constants and query DOM outside animation functions
const canvas = document.getElementById("animCanvas");
const ctx = canvas.getContext("2d");
Math.PI2 = Math.PI * 2; 
var startTime;

restartAnimate();

function restartAnimate() {
    if (startTime === undefined) {
        requestAnimationFrame(runAnimation);
    } else {
        startTime = 0;  // next frame animation we have restarted
    }
    // setTimeout(restartAnimate(),1000); // No need to restart as angle is cyclic.
}

function runAnimation(time) {
    if (!startTime) { startTime = time }
    const currentTime = time - startTime;
    ctx.setTransform(1,0,0,1,0,0); // resets transform, better than using save and restore
    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); // avoid magic numbers
    //ctx.save(); // not needed

    ctx.setTransform(1,0,0,1,150, 75); // last two values set the origin
                                       // and is the point we rotate around
    ctx.strokeStyle = "#99ebff";
    ctx.lineWidth = 10;
    ctx.beginPath();
    ctx.arc(0, 0, 40, 0, Math.PI2);  // rendering at the origin
    ctx.stroke();

    //ctx.restore(); // not needed
    //ctx.save();  // not needed
    ctx.fillStyle = "#000000";
    //ctx.translate(150,75);   // working from origin so don't need to translate
    ctx.rotate(Math.PI2 * currentTime / 1000);
    //ctx.translate(-150,-75); // working from origin so don't need to translate
    ctx.beginPath();
    ctx.arc(36.5, 0 ,13, 0, Math.PI2);
    ctx.fill();

    //ctx.restore(); not needed

    requestAnimationFrame(runAnimation);
}
<canvas id="animCanvas"></canvas>

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.