Riley Christian
Riley Christian

Reputation: 225

I need to animate the concetric circles on a canvas FabricJs using recursive function

I have 10 concentric circles that i want to animate using FabricJs Canvas. My goal is that the 1st inner circle should show itself first then and the 2nd, and so on by using recursion or some other techniques to avoid code duplication. I have managed to animates two 1st inner circles but with redundant code.

const canvas = new fabric.Canvas('gameCanvas', {
  selection: false
});

fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

let circles = [];

document.addEventListener('DOMContentLoaded', function() {
  drawCircles();
});

document.getElementById('animateBtn').addEventListener('click', function() {
 animateCircles();
});

function makeCircle(r) {
  return new fabric.Circle({
    left: 300,
    top: 120,
    strokeWidth: 1.5,
    radius: r,
    fill: 'white',
    stroke: 'black',
    selectable: false,
    hoverCursor: 'default',
    hasControls: false,
    hasBorders: false
  });

}

function drawCircles()
{
   for(let i = 9; i >= 0; i--)
     {
       let circle  = makeCircle(10*(i + 1));
         circles.push(circle);
         canvas.add(circle);
     }
   
     
}

function animateCircles()
{
    canvas.clear();
  
    
    circles[9].animate({
            opacity: 1
        },
        {
            duration: 2000,
            easing: fabric.util.ease['easeInElastic'],
            onChange: canvas.renderAll.bind(canvas),
            onComplete: function () {

                circles[9].setCoords();
                canvas.add(circles[9]);
                canvas.renderAll();
                circles[8].animate({
                    opacity: 1
                },
                {
                    duration: 2000,
                        easing: fabric.util.ease['easeInElastic'],
                        onChange: canvas.renderAll.bind(canvas),
                        onComplete: function () {
                            circles[8].setCoords();
                                canvas.add(circles[8]);
                                canvas.renderAll();
                                //On this line i need to use the code for animation
                        }
                }
                
                );
        }
    });
}
#container {
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
}

#animateBtn {
    margin-top: .5em;
}
<div id="container">
    <canvas id="gameCanvas" width="600" height="240" style="border: 2px solid green;"></canvas>
  <button id="animateBtn">Start Animation</button>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.0.0/fabric.min.js"></script>

Upvotes: 1

Views: 167

Answers (1)

trincot
trincot

Reputation: 350252

Create a function loop(i) which has the repeating code (so without the initial clear() call) for one circle. Let i be the index of the circle. Then in the onComplete callback, call that loop, but now with argument i-1. Add a stop condition (when i would become negative).

Here is your code with that implementation:

const canvas = new fabric.Canvas('gameCanvas', {
  selection: false
});

fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

let circles = [];

document.addEventListener('DOMContentLoaded', drawCircles);
document.getElementById('animateBtn').addEventListener('click', animateCircles);

function makeCircle(r) {
  return new fabric.Circle({
    left: 300,
    top: 120,
    strokeWidth: 1.5,
    radius: r,
    fill: 'white',
    stroke: 'black',
    selectable: false,
    hoverCursor: 'default',
    hasControls: false,
    hasBorders: false
  });
}

function drawCircles() {
    for (let i = 9; i >= 0; i--) {
        let circle  = makeCircle(10*(i + 1));
        circles.push(circle);
        canvas.add(circle);
    }
}

function animateCircles() {
    canvas.clear();
    
    function loop(i) {
        if (i < 0) return; // end the asynchronous loop
        circles[i].animate({
            opacity: 1
        }, {
            duration: 300,
            easing: fabric.util.ease['easeInElastic'],
            onChange: canvas.renderAll.bind(canvas),
            onComplete: function () {
                circles[i].setCoords();
                canvas.add(circles[i]);
                canvas.renderAll();
                loop(i - 1);
            }
        });
    }
    
    loop(9); // start the asynchronous loop
}
#container {
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
}

#animateBtn {
    margin-top: .5em;
}
<div id="container">
    <canvas id="gameCanvas" width="600" height="240" style="border: 2px solid green;"></canvas>
  <button id="animateBtn">Start Animation</button>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.0.0/fabric.min.js"></script>

Upvotes: 1

Related Questions