- const randomInRange = (min, max) => Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1) + Math.ceil(min));

-
  const getHue = input => {
    if (input > 0 && input <= 25) return 18
    if (input > 25 && input <= 50) return 130
    if (input > 50 && input <= 75) return 44
    if (input > 75 && input <= 100) return 215
  }

mixin section
  section
    if block
      block

main
  h1 Tap to replay
  - const PARTICLE_COUNT = 14;
  - const PARTICLES_PER_PEN = 30;
  +section
    .sling-ring.sling-ring--particles.sling-ring--particles-real(
      style=`--particle-count: ${PARTICLE_COUNT}`
    )
      .sling-ring__ring.sling-ring__ring--particles
        - let e = 0;
        while e < PARTICLE_COUNT
          .sling-ring__particle-pen(
            style=`--index: ${e}; --base-delay: ${e / 5};`
          )
            .sling-ring__particle-start
              - let pe = 0;
              while pe < PARTICLES_PER_PEN
                - const SIZE = randomInRange(10, 80);
                - const DISTANCE = randomInRange(3, 7);
                - const X = randomInRange(0, 100);
                - const Y = randomInRange(0, 100);
                - const ROTATION = randomInRange(-30, 0);
                - const RADIUS = randomInRange(0, 50);
                - const DELAY = randomInRange(0, 100) / 100;
                - const HUE = getHue(Math.round((e / PARTICLE_COUNT) * 100));
                - const LIGHTNESS = randomInRange(20, 90);
                .sling-ring__particle(
                  style=`--radius: ${RADIUS}; --hue: ${HUE}; --lightness: ${LIGHTNESS}; --size: ${SIZE}; --distance: ${DISTANCE}; --x: ${X}; --y: ${Y}; --delay: ${DELAY}; --rotation: ${ROTATION};`
                )
                - pe++;
          - e++;
      img(src="https://assets.codepen.io/605876/come-see--square.gif")
View Compiled
@property --ring-reveal {
  inherits: true;
  initial-value: 0%;
  syntax: '<percentage>';
}

:root {
  --size: 35vmin;
  --ring-thickness: calc(var(--size) * 0.2);
  --red: #ff4d00;
  --green: #38b24d;
  --yellow: #ebb624;
  --blue: #0f73ff;
  --base-speed: 1;
}

*,
*:after,
*:before {
  box-sizing: border-box;
}

body {
  background: hsl(32 61% 94%);
  display: grid;
  place-items: center;
  min-height: 100vh;
  font-family: 'Google Sans', sans-serif, system-ui;
  padding: 10vmin;
}
main {
  width: 100%;
  display: grid;
  place-items: center;
}
h1 {
  color: hsl(32 64% 66%);
  position: fixed;
  margin: 0;
  font-family: 'Google Sans', sans-serif, system-ui;
  bottom: 1rem;
  right: 1rem;
}

section {
  width: var(--size);
  aspect-ratio: 1;
  position: relative;
  animation: scale-in calc(var(--base-speed) * 4s) both;
}

@keyframes scale-in {
  0% {
    transform: scale(0);
  }
}
.sling-ring {
  height: 100%;
  aspect-ratio: 1;
  position: relative;
}
.sling-ring__ring {
  --base-rotation: -120deg;
  --starting-point: calc(360 * -0.125deg);
  --ring-reveal: 0%;
  height: 100%;
  width: 100%;
  border-radius: 50%;
  position: absolute;
  transform: rotate(0deg);
  border-color: var(--red) var(--green) var(--yellow) var(--blue);
  mask: conic-gradient(from var(--starting-point), #000 var(--ring-reveal), transparent var(--ring-reveal));
  -webkit-mask: conic-gradient(from var(--starting-point), #000 var(--ring-reveal), transparent var(--ring-reveal));
  animation: reveal calc(var(--base-speed) * 1s) both ease-in, rotate 10s infinite linear;
  z-index: 2;
}

img {
  height: 100%;
  width: 100%;
  object-fit: cover;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  animation: fade-in calc(var(--base-speed) * 1s) calc(var(--base-speed) * 0.25s) ease-in both;
}

.sling-ring__ring--particles {
  border: none;
  -webkit-mask: none;
}
.sling-ring__particle-pen {
  aspect-ratio: 1;
  position: absolute;
  inset: calc(var(--ring-thickness) * -1);
  transform-origin: center;
  transform: rotate(calc(((360 / var(--particle-count)) * var(--index)) * 1deg));
}
.sling-ring__particle-start {
  position: absolute;
  height: calc(var(--ring-thickness) * 1);
  aspect-ratio: 1;
  top: 0;
  left: 50%;
  transform: translate(-50%, 50%) rotate(-90deg);
  background: rgba(255,0,0,0.2);
}
.sling-ring__particle {
  height: calc(var(--size, 100) * 1%);
  aspect-ratio: 1;
  background: hsl(var(--hue, 280), 100%, calc(var(--lightness, 50) * 1%));
  position: absolute;
  top: calc(var(--x, 50) * 1%);
  left: calc(var(--y, 50) * 1%);
  animation: blast 0.85s infinite both ease-out, fade-away 0.85s infinite both;
  animation-delay: calc((var(--base-delay, 0) + var(--delay, 0)) * 1s);
}
.sling-ring--particles-real .sling-ring__particle {
  border-radius: calc(var(--radius, 50) * 1%);
}
.sling-ring--particles-real .sling-ring__particle-start {
  background: none;
}
.sling-ring--particles-real img {
  animation-delay: calc((var(--particle-count) / 5) * 0.75s);
}


@keyframes reveal {
  to {
    --ring-reveal: 100%;
  }
}

@keyframes rotate-in {
  from {
    transform: rotate(var(--base-rotation, 0deg));
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes rotate {
  to {
    transform: rotate(360deg);
  }
}

@keyframes blast {
  from {
    transform: rotate(calc(var(--rotation, 0) * 1deg)) scale(0) translate(0, 0);
  }
  to {
    transform: rotate(calc(var(--rotation, 0) * 1deg)) scale(1.2) translate(0, calc((var(--ring-thickness) * var(--distance, 5)) * -1));
  }
}

@keyframes fade-away {
  0%, 75% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
const REPLAY = () => {
	const MARKUP = document.body.innerHTML
	document.body.innerHTML = ''
	requestAnimationFrame(() => document.body.innerHTML = MARKUP)
}

document.body.addEventListener('click', REPLAY)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.