Reputation: 225
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
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