Cool Profile Picture Animation in HTML, CSS, and JS

Cool Profile Picture Animation in HTML, CSS, and JS

I saw this cool pixilation filter in the About Us section of a landing page, but it was static and boring. So, I created this effect using pure HTML, CSS, and JS and animated it on hover to reveal the faces.

The Demo

Photo by Alex Suprun on Unsplash

The Code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Pixel Profile Pic test</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
        background-color: #000814;
      }

      .main_container {
        width: 100%;
        min-height: 100vh;
        overflow: hidden;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .outerImage {
        width: 400px;
        height: 400px;
        display: flex;
        justify-content: center;
        align-items: center;
        background-image: url("/pi.png");
        background-size: cover;
        background-position: center;
        overflow: hidden;
        border-radius: 20%;
      }

      .innerImage {
        width: 100%;
        height: 100%;
        background-size: cover;
        background-position: center;
        clip-path: polygon(20% 20%, 80% 20%, 80% 80%, 20% 80%);
        border-radius: 20%;
      }
    </style>
  </head>
  <body>
    <div class="main_container">
      <div
        class="outerImage"
        onmouseenter="removePixelation()"
        onmouseleave="applyPixelation()"
      >
        <canvas id="canvas" class="innerImage"></canvas>
      </div>
    </div>

    <script>
      var img = new Image();
      img.src = "/pi.png";
      img.onload = function () {
        console.log("image loaded");
        draw(this);
      };

      var pixels = [];

      var canvas = document.getElementById("canvas");
      var ctx = canvas.getContext("2d");
      var size = 30; // size of the pixels
      var imgData;

      function draw(img) {
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0, img.width, img.height);
        imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        applyPixelation();
      }

      var originalImagel;
      function applyPixelation() {
        console.log("applying pixelation");

        originalImagel = ctx.getImageData(0, 0, canvas.width, canvas.height);
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Create an array of all pixel positions
        pixels = [];
        for (var i = 0; i < imgData.width; i += size) {
          for (var j = 0; j < imgData.height; j += size) {
            pixels.push({ x: i, y: j });
          }
        }

        // Shuffle the array
        pixels.sort(() => Math.random() - 0.5);

        var index = 0;
        function drawPixel() {
          for (let k = 0; k < 4; k++) {
            // Draw 4 pixels at a time
            if (index >= pixels.length) {
              return; // All pixels have been drawn
            }
            var pixel = pixels[index++];
            var color = getAverageColor(pixel.x, pixel.y, size, imgData);
            ctx.fillStyle =
              "rgb(" + color.r + "," + color.g + "," + color.b + ")";
            ctx.fillRect(pixel.x, pixel.y, size, size);
          }
          setTimeout(drawPixel, 0); // Call drawPixel again after a delay
        }
        drawPixel(); // Start the drawing process
      }

      //   function removePixelation() {
      //     ctx.putImageData(imgData, 0, 0);
      //   }
      function removePixelation() {
        if (!pixels || pixels.length === 0) {
          // Reinitialize the pixels array if needed
          pixels = [];
          for (var i = 0; i < imgData.width; i += size) {
            for (var j = 0; j < imgData.height; j += size) {
              pixels.push({ x: i, y: j });
            }
          }
          // Reverse the order of the pixels to un-pixelate from the last to the first
          pixels.reverse();
        }

        var index = 0;
        function revealPixel() {
          for (let k = 0; k < 4; k++) {
            // Reveal multiple pixels at a time for a faster animation
            if (index >= pixels.length) {
              return; // All pixels have been revealed
            }
            var pixel = pixels[index++];
            // Clear the specific pixel block
            ctx.clearRect(pixel.x, pixel.y, size, size);
            // Draw the original pixels within each block from the stored original image
            ctx.putImageData(
              originalImagel,
              -pixel.x,
              -pixel.y,
              pixel.x,
              pixel.y,
              size,
              size
            );
          }
          setTimeout(revealPixel, 0); // Call revealPixel again after a delay
        }
        revealPixel(); // Start the revealing process
      }

      function getAverageColor(x, y, size, imgData) {
        var total = { r: 0, g: 0, b: 0, count: 0 };
        for (var i = 0; i < size; i++) {
          for (var j = 0; j < size; j++) {
            var idx = (x + i + (y + j) * imgData.width) * 4;
            total.r += imgData.data[idx + 0];
            total.g += imgData.data[idx + 1];
            total.b += imgData.data[idx + 2];
            total.count++;
          }
        }
        return {
          r: (total.r / total.count) | 0,
          g: (total.g / total.count) | 0,
          b: (total.b / total.count) | 0,
        };
      }
    </script>
  </body>
</html>

The Conclusion

I added this cool animated pixel effect to my portfolio site. It makes the profile picture come to life when you hover over it. Check it out at daviddodda.com.

and shutout to @domi_kissi and @inCleveri for the inspiration.