user2617915
user2617915

Reputation: 43

Draw arc with increasing radius?

I am drawing an arc which increases gradually and turns in to a circle.On completion of animation(arc turning in to a circle) i want to draw another circle with increased radius with the previous circle persisting and the second animation continuing.

Arc to circle fiddle

After the circle is drawn,it gets washed out which is something that I dont want and continue the second animation. Some unnecessary animation appears after the completion.

What should I do?

MyCode:

    setInterval(function(){
        context.save();
        context.clearRect(0,0,500,400);
        context.beginPath();
        increase_end_angle=increase_end_angle+11/500;
        dynamic_end_angle=end_angle+increase_end_angle;
        context.arc(x,y,radius,start_angle,dynamic_end_angle,false);
        context.lineWidth=6;
        context.lineCap = "round";
        context.stroke();
        context.restore();
           if(dynamic_end_angle>3.5*Math.PI){  //condition for if circle completion
                draw(radius+10);//draw from same origin and increasd radius
           }
    },66);

window.onload=draw(30);

UPDATE:when should i clear the interval to save some cpu cycles and why does the animation slows down on third circle ??

Upvotes: 4

Views: 1625

Answers (3)

user1693593
user1693593

Reputation:

When should I clear the interval to save some cpu cycles?

Better yet not use an interval at all for a couple of reasons:

  • Intervals are unable to sync to monitor's VBLANK gap so you will get jerks from time to time.
  • If you use setInterval you risk stacking calls (not high risk in this case though).

A much better approach is as you probably already know to use requestAnimationFrame. It's less CPU hungry, is able to sync to monitor and uses less resources in general even less if current tab/window is not active.

Why does the animation slows down on third circle ??

Your drawing calls are accumulating which slows everything down (setInterval is not cleared).

Here is a different approach to this. It's a simplified way and uses differential painting.

ONLINE DEMO

The main draw function here takes two arguments, circle index and current angle for that circle. The circles radius are stored in an array:

...,
sa = 0,                   // start angle
ea = 359,                 // end angle
angle = sa,               // current angle
oldAngle = sa,            // old angle
steps = 2,                // number of degrees per step
current = 0,              // current circle index
circles = [70, 80, 90],   // the circle radius
numOfCircles = circles.length, ...

The function stores the old angle and only draws a new segment between old angle and new angle with 0.5 added to compensate for glitches due to anti-alias, rounding errors etc.

function drawCircle(circle, angle) {

    angle *= deg2rad; // here: convert to radians

    /// draw arc from old angle to new angle
    ctx.beginPath();
    ctx.arc(0, 0, circles[circle], oldAngle, angle + 0.5);
    ctx.stroke();

    /// store angle as old angle for next round
    oldAngle = angle;
}

The loop increases the angle, if above or equal to end angle it will reset the angle and increase the current circle counter. When current counter has reach last circle the loop ends:

function loop() {

    angle += steps;

    /// check angle and reset, move to next circle        
    if (angle >= ea - steps) {
        current++;
        angle = sa;
        oldAngle = angle;
    }

    drawCircle(current, angle);

    if (current < numOfCircles)
        requestAnimationFrame(loop);
}

Upvotes: 1

zs2020
zs2020

Reputation: 54514

This snippet from your code has some flaw.

if(dynamic_end_angle>3.5*Math.PI){  //condition for if circle completion
    draw(radius+10);//draw from same origin and increased radius
}

The recursive call to draw() will continue to run after the first circle was drawn completely. This is why the performance will be slow down immediately. You need to somehow block it.

I did a simple fix, you can polish it if you like. FIDDLE DEMO

My fix is to remove context.clearRect(0, 0, 500, 400); and change the new circle drawing logic to:

if (dynamic_end_angle > 3.5 * Math.PI) { //condition for if circle completion
    increase_end_angle = 0; // this will prevent the draw() from triggering multiple times.
    draw(radius + 10); //draw from same origin.
}

In this stackoverflow thread, it mentions how to make it more smooth. You'd better use some drawing framework since the optimization needs a lot of work.

Upvotes: 3

Ry-
Ry-

Reputation: 224903

First of all, about the flicker: you are using setInterval and not clearing it for the next draw(). So there’s that.

But I’d use a completely different approach; just check the time elapsed since the start, and draw an appropriate number of circles using a loop.

var start = new Date().getTime();

var timePerCircle = 2;
var x = 190, y = 140;

function draw() {
    requestAnimationFrame(draw);
    g.clearRect(0, 0, canvas.width, canvas.height);

    var t = (new Date().getTime() - start) / 1000;
    var circles = t / timePerCircle;
    var r = 30;

    do {
        g.beginPath();
        g.arc(x, y, r, 0, Math.PI * 2 * Math.min(circles, 1));
        g.stroke();
        r += 10;
        circles--;
    } while(circles > 0);
}

draw();

Upvotes: 3

Related Questions