彩色像素骷髅

Published on
/
/趣玩前端
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>彩色像素骷髅</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"
    />
    <style>
      #count {
        display: none;
      }

      canvas {
        display: none;
      }

      :root {
        --tile-size: 0.7vmin;
        --distance: 1vmin;
      }

      @property --x {
        syntax: '<length>';
        initial-value: 0;
        inherits: true;
      }
      @property --y {
        syntax: '<length>';
        initial-value: 0;
        inherits: true;
      }
      @property --scale {
        syntax: '<number>';
        initial-value: 1;
        inherits: true;
      }
      #tiles {
        filter: drop-shadow(0 0 1rem black);
        display: grid;
        place-items: center;
        cursor: pointer;
        translate: 0 -12vh;
      }
      #tiles > div {
        border-radius: 50%;
        width: var(--tile-size);
        aspect-ratio: 1;
        background: hsla(var(--hue), 100%, calc(50% + var(--light) * 50%), 1);
        position: absolute;
        transform: translate3d(var(--x), var(--y), 0) scale(var(--scale));
        --delay: calc(var(--p-r) * 1s);
        --hue: calc(var(--p-r) * (361 - 80) + 80);
        --light: calc((var(--p-r) * (0.5 - 0.05) + -0.05));
        --duration: calc(var(--p-r2) * 10s);
      }
      #tiles.show > div {
        -webkit-animation: fade-in 0.3s linear forwards,
          show var(--duration) var(--delay) cubic-bezier(0.86, 0.86, 0.41, 1.16)
            infinite,
          blink 1s var(--delay) ease-in-out infinite;
        animation: fade-in 0.3s linear forwards,
          show var(--duration) var(--delay) cubic-bezier(0.86, 0.86, 0.41, 1.16)
            infinite,
          blink 1s var(--delay) ease-in-out infinite;
      }

      @-webkit-keyframes blink {
        from,
        50%,
        to {
          opacity: 1;
        }
        30%,
        70% {
          opacity: 0.3;
        }
      }

      @keyframes blink {
        from,
        50%,
        to {
          opacity: 1;
        }
        30%,
        70% {
          opacity: 0.3;
        }
      }
      @-webkit-keyframes fade-in {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }
      @keyframes fade-in {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }
      @-webkit-keyframes show {
        from {
          --x: calc(1dvw * var(--p-r));
          --y: calc(-100dvh * var(--p-r));
          filter: blur(0.5rem);
          --scale: calc(clamp(1.5, 10 * var(--p-r2), 5));
        }
        5% {
          filter: blur(0rem);
        }
        10% {
          --x: calc(var(--p-x) * var(--distance));
          --y: calc(var(--p-y) * var(--distance));
          --scale: calc(1);
        }
        to {
          --x: calc(var(--p-x) * var(--distance));
          --y: calc(var(--p-y) * var(--distance));
        }
      }
      @keyframes show {
        from {
          --x: calc(1dvw * var(--p-r));
          --y: calc(-100dvh * var(--p-r));
          filter: blur(0.5rem);
          --scale: calc(clamp(1.5, 10 * var(--p-r2), 5));
        }
        5% {
          filter: blur(0rem);
        }
        10% {
          --x: calc(var(--p-x) * var(--distance));
          --y: calc(var(--p-y) * var(--distance));
          --scale: calc(1);
        }
        to {
          --x: calc(var(--p-x) * var(--distance));
          --y: calc(var(--p-y) * var(--distance));
        }
      }
      body {
        width: 100vw;
        height: 100vh;
        display: grid;
        place-items: center;
        background: conic-gradient(at 50% 52%, #180a3e, black, black, #180a3e);
        color: #ee75d2;
        margin: 0;
        overflow: clip;
        cursor: pointer;
      }

      * {
        box-sizing: border-box;
      }

      a.labs-follow-me {
        left: 2rem;
        right: 2rem;
        bottom: 1rem;
        top: unset;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div id="tiles" class="show"></div>
    <div id="count" class="content"></div>
    <canvas id="canvas"></canvas>

    <script>
      const counts = count.innerText;
      const size = 110;
      const ctx = canvas.getContext('2d');
      const font = `bold ${size}px monospace`;
      ctx.font = font;
      const metrics = ctx.measureText(counts);
      canvas.width = metrics.width;
      canvas.height = size;

      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.font = font;
      ctx.fillStyle = 'black';
      ctx.fillText(counts, 0, canvas.height);

      const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const length = data.length;
      const pixels = [];
      let x = 0,
        y = 0;
      const centerX = canvas.width / 2;
      const centerY = canvas.height / 2;
      for (let i = 0; i < length; i += 4) {
        const pixel = {
          hit: data[i] !== 255,
          i,
          x: x - centerX,
          y: y - centerY,
        };
        if (data[i] === 0) {
          pixels.push(pixel);
        }
        x++;
        if (x === canvas.width) {
          x = 0;
          y++;
        }
      }
      pixels.forEach(({ x, y }, index) => {
        const tile = document.createElement('div');
        tile.style.setProperty('--p-x', `${x}`);
        tile.style.setProperty('--p-y', `${y}`);
        tile.style.setProperty('--p-i', `${index}`);
        tile.style.setProperty('--p-r', `${Math.random()}`);
        tile.style.setProperty('--p-r2', `${Math.random()}`);
        tiles.appendChild(tile);
      });

      document.addEventListener('click', () => {
        tiles.classList.remove('show');
        requestAnimationFrame(() => {
          tiles.classList.add('show');
        });
      });
    </script>
  </body>
</html>