Juggling

by Grzegorz Matyszewski

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.3/gsap.min.js"></script>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>

CSS

li.circle {
  position: absolute;
  list-style: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid currentcolor;
  text-align: center;
  line-height: 40px;
  counter-increment: li;
  margin: -20px;
  font-family: monospace;
  color: white;
}

li.circle::before { content: counter(li); }

body { margin: 0; padding: 0; background: #fff; }

JavaScript

const options = {
	amount: 8,
  offset: 125,
  radius: 100,
  shift: 100,
}

const items = [...document.querySelectorAll('li')];

function draw() {
  const step = 360 / options.amount;	
  const {radius, offset, amount, shift} = options;
  const center = { x: window.innerWidth * 0.5, y: window.innerHeight * 0.5 };
  
	items.forEach((el, i) => {
  		let angle = (offset + i * step);
      angle -= Math.sin((90 + angle) / 180 * Math.PI) * shift * step / 150;
      const x = center.x + radius * Math.cos(angle * Math.PI / 180);
      const y = center.y + radius * Math.sin(angle * Math.PI / 180);
      el.style.left = x  + 'px';
      el.style.top = y + 'px';
      el.style.display = i >= amount ? 'none': '';
  });
}

/* draw(); */

const gui = new dat.GUI();
gui.add(options, 'radius').min(0).max(200).onChange(draw);
gui.add(options, 'amount').min(3).max(20).step(1).onChange(() => {
draw(); setColors(); });
gui.add(options, 'offset').min(0).max(360).onChange(draw).listen();
gui.add(options, 'shift').min(0).max(100).onChange(draw).name('distortion');

const timeline = gsap.timeline({ onUpdate: draw, repeat: -1 });
timeline.fromTo(options, {
  offset: 0,
}, {
  offset: 359,
  ease: 'none',
  duration: 3,
});

gui.add(timeline, 'play');
gui.add(timeline, 'pause');
gui.close();

function setColors() {
  items.forEach((el, i) => {
    const hue = 360 / options.amount * i;
    el.style.backgroundColor = `hsl(${hue}deg, 90%, 60%)`;
  });
}
setColors();