JSFiddle - React, Tailwind, and code Playground

by Tintu Raju

HTML

<div class="item disintegration-target">
  Welcome
</div>

<div class="item disintegration-target">
  Testing
</div>

<div class="item disintegration-target">
  Testing another 
</div>


<div class="item disintegration-target">
  <img...

SCSS

.disintegration-container {
  position: absolute;
  pointer-events: none;
  
  > canvas {
    position: absolute;
    left: 0;
    top: 0;
    transition: transform 1s ease-out, opacity 1s ease-out;
    opacity: 1;
    transform:
      rotate(0deg) translate(0px, 0px) rotate(0deg);
  }
}

@keyframes debug-pulse {
  0% { filter: none; }
  95% { filter: none; }
  95% { filter: drop-shadow(0 1px 0 rgba(255,0,0,1)); }
  100% { filter: drop-shadow(0 1px 0 rgba(255,0,0,1)); }
}

Babel + JSX

const DEBUG = false;
const REPETITION_COUNT = 2; // number of times each pixel is assigned to a canvas
const NUM_FRAMES = 128;

/**
 * Generates the individual subsets of pixels that are animated to create the effect
 * @param {HTMLCanvasElement} ctx
 * @param {number} count The higher the frame count, the less grouped the pixels will look - Google use 32, but for our elms we use 128 since we have images near the edges
 * @return {HTMLCanvasElement[]} Each canvas contains a subset of the original pixels
 */
function generateFrames($canvas, count = 32) {
  const { width, height } = $canvas;
  const ctx = $canvas.getContext("2d");
  const originalData = ctx.getImageData(0, 0, width, height);
  const imageDatas = [...Array(count)].map(
    (_,i) => ctx.createImageData(width, height)
  );
  
  // assign the pixels to a canvas
  // each pixel is assigned to 2 canvas', based on its x-position
  for (let x = 0; x < width; ++x) {
    for (let y = 0; y < height; ++y) {
      for (let i = 0; i < REPETITION_COUNT; ++i) {
        const dataIndex = Math.floor(
          count * (Math.random() + 2 * x / width) / 3
        );
        const pixelIndex = (y * width + x) * 4;
        // copy the pixel over from the original image
        for (let offset = 0; offset < 4; ++offset) {
          imageDatas[dataIndex].data[pixelIndex + offset]
            = originalData.data[pixelIndex + offset];
        }
      }
    }
  }
  
  // turn image datas into canvas'
  return imageDatas.map(data => {
    const $c = $canvas.cloneNode(true);
    $c.getContext("2d").putImageData(data, 0, 0);
    return $c;
  });
}

/**
 * Inserts a new element over an old one, hiding the old one
 */
function replaceElementVisually($old, $new) {
  const $parent = $old.offsetParent;
  $new.style.top = `${$old.offsetTop}px`;
  $new.style.left = `${$old.offsetLeft}px`;
  $new.style.width = `${$old.offsetWidth}px`;
  $new.style.height = `${$old.offsetHeight}px`;
  $parent.appendChild($new);
  $old.style.visibility =...