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