{
const width = 1000;
const height = 900;
const radius = 1.7;
const padding = 1;
const n = 10000;
const play = document.querySelector("#play");
const pause = document.querySelector("#pause");
const restart = document.querySelector("#restart");
const reverse = document.querySelector("#reverse");
const c = DOM.context2d(width, height);
c.translate(250, 250);
const circles = d3.packSiblings(d3.range(n).map(() => ({
r: radius + padding,
fill: '#ffafcc'
})));
circles.forEach((circle, i) => {
circle.group = i % 3 === 0? 'pandas' : 'cats'
})
const groups = ['pandas', 'cats']
const circlesPerGroup = {}
for (const group of groups) {
circlesPerGroup[group] = d3.packSiblings(d3.range(
circles.filter(circle => circle.group === group).length
).map(() => ({
r: radius + padding,
group: group
})))
};
// 2. Create the new circles by looping over the groups
const xOffset = 500; // how far right to move horizontally from initial position
const yOffsetPerGroup = 350; // this can also be defined with a scale if we have more groups
const movedCircles = []
for (const [group, groupCircles] of Object.entries(circlesPerGroup)) {
const groupNumber = groups.indexOf(group)
groupCircles.forEach((circle, index) => {
movedCircles.push({
x: groupCircles[index].x + xOffset,
y: groupCircles[index].y + yOffsetPerGroup*groupNumber,
fill: circle.group === 'cats'? '#76c893' : '#1a759f', // can be done with a colour scheme
group: circle.group
})
})
}
/// Animation ///
// animate the x,y pos and the fill s.t. x and y are moved to the position of the moved circles,
// which we can access via the index; stagger the particles
const tl = gsap.to(circles, {
x: (index, target, targets) => movedCircles[index].x,
y: (index, target, targets) => movedCircles[index].y,
fill: (index, target, targets) => movedCircles[index].fill,
duration: 1,
ease: 'power.3.out',
stagger: { amount: 2 },
onUpdate: animate
});
tl.pause() // pause to begin with; play if button is clicked
// this is what happens on update of the gsap animation
function animate() {
c.fillStyle = "#22223b"
c.fillRect(-250, -250, 1200, 1200); // make sure to clear or fill the rect
for (const { x, y, r, fill } of circles) {
c.beginPath();
c.arc(x, y, r-padding, 0, 2*Math.PI)
c.fillStyle = fill
c.fill()
}
}
// run the drawing logic once just so that we can see the initial circle packing
animate()
// button controls to be able to play/pause etc the gsap animation
play.onclick = () => tl.play();
restart.onclick = () => tl.restart()
pause.onclick = () => tl.pause();
reverse.onclick = () => tl.reverse();
yield c.canvas;
}