Skip to main content
under engineered

Re-creating Google Thanos snap animation

If you haven't seen this animation it's probably better to check it out first.

Search "Thanos" on Google, and you'll see a golden hand, tap/click on it to see the magic.

step - 1: create a basic DOM structure #

<div class="container">
  <div class="result">
    <div class="content">
      <h2>Some heading</h2>
      <p>
        Duis venenatis sapien id massa fermentum, ac sollicitudin augue
        vestibulum. Vestibulum sapien nunc, convallis nec commodo sit amet,
        semper in urna. Curabitur scelerisque elit quis libero viverra
        ultricies.
      </p>
    </div>
  </div>
</div>

step - 2: nail the disappearing act #

const searchElement = document.querySelector(".content");
const parent = searchElement.parentNode;
const clone1 = searchElement.cloneNode(true);
const clone2 = searchElement.cloneNode(true);
.relative {
  position: relative;
}

.absolute {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  opacity: 0.7;
  z-index: -1;
  will-change: transform;
  transition: transform 2s ease-out 0s, opacity 1.2s ease-out 0s;
}

I've also added a transition property to class absolute so as to animate them using transforms

parent.classList.add("relative");
clone1.classList.add("absolute");
clone2.classList.add("absolute");
// finally append to parent
parent.append(clone1, clone2);
.fade {
  opacity: 0;
}
.slide-left {
  transform: translateX(-100px);
}
.slide-right {
  transform: translateX(100px);
}
searchElement.classList.add("fade");

// slide the clones in the next frame
setTimeout(() => {
  clone1.classList.add("slide-left", "fade");
  clone2.classList.add("slide-right", "fade");
}, 0);
const removeNode = (node) => {
  node.addEventListener("transitionend", () => {
    node.remove();
  });
};
removeNode(clone1);
removeNode(clone2);

we're almost there!

step - 3: doing this randomly for half the search result, because you know Thanos! #

const snapButton = document.querySelector(".big-button");
snapButton.addEventListener("click", () => {
  const allSearchElements = [...document.querySelectorAll(".content")];

  // randomize returns random results array with half the length of original one
  const half = randomize(allSearchElements);
  // start the chain reaction
  half.reduce(async (promise, curr) => {
    // waiting for one result to vanish!
    await promise;
    return scrollTo(curr).then(() => vanish(curr));
  }, Promise.resolve());
});

thanos.gif

discuss on twitter, because why not?