How to Build a Stunning Like‑Button Particle Animation with Pure CSS

This tutorial walks through creating a eye‑catching like‑button effect using only CSS and a bit of JavaScript, covering parabolic particle motion, an optional oblique bounce, batch generation of random emojis, a +1 number animation, and a brief note on canvas alternatives.

Full-Stack Cultivation Path
Full-Stack Cultivation Path
Full-Stack Cultivation Path
How to Build a Stunning Like‑Button Particle Animation with Pure CSS

1. CSS Parabolic Motion

The effect consists of two animations: a particle following a parabolic trajectory and a number scaling animation. A parabolic motion is a combination of constant horizontal speed and constant vertical acceleration. Using two nested divs, the horizontal movement uses a linear timing function while the vertical movement uses a cubic‑bezier curve to simulate acceleration.

<div class="ball-x">
  <div class="ball-y"></div>
</div>

Key CSS:

.ball-x { animation-timing-function: linear; }
.ball-y { animation-timing-function: cubic-bezier(0.55,0,0.85,0.36); }
@keyframes custom-x { 100% { transform: translateX(300%); } }
@keyframes custom-y { 100% { transform: translateY(300%); } }

2. CSS Oblique Motion

To add an upward launch and a slight bounce, the cubic‑bezier curve is adjusted so the second control point becomes negative, creating a reverse acceleration phase.

.ball-y { animation-timing-function: cubic-bezier(0.56,-1.35,0.85,0.36); }

This produces a subtle rebound that more closely resembles a real projectile.

3. Batch Generation with JavaScript

One particle looks static, so the tutorial shows how to generate many random particles on each click. JavaScript creates a document fragment, picks a random subset of emojis, and assigns CSS variables --x (horizontal offset) and --d (delay) to each dot. An animationend listener removes finished dots.

function createDots(emojis) {
  const fragment = document.createDocumentFragment();
  const randomEmojis = emojis.slice(0, Math.ceil(Math.random()*emojis.length))
    .sort(() => Math.random() - .5);
  randomEmojis.forEach(emoji => {
    const dot = document.createElement('div');
    dot.className = 'custom-tips-dot';
    dot.setAttribute('emoji', emoji);
    dot.style.setProperty('--d', `${Math.random()*0.2}s`);
    dot.style.setProperty('--x', `${(Math.random()-0.5)*1000}%`);
    fragment.appendChild(dot);
    dot.addEventListener('animationend', () => {
      dot.parentNode?.removeChild(dot);
    });
  });
  return fragment;
}

document.addEventListener('click', ev => {
  const {clientX, clientY} = ev;
  document.body.style.setProperty('--left', `${clientX}px`);
  document.body.style.setProperty('--top', `${clientY}px`);
  const tips = document.createElement('div');
  tips.className = 'custom-tips';
  tips.style.setProperty('--left', `${clientX}px`);
  tips.style.setProperty('--top', `${clientY}px`);
  const dots = createDots(['🎉','😘','🎊','🤡','🥳','🤪','💗']);
  tips.appendChild(dots);
  document.body.appendChild(tips);
});

4. "+1" Number Animation

The numeric animation is a simple scale‑and‑opacity change. The HTML element carries a num attribute, and a ::before pseudo‑element displays "+" followed by that number.

<div class="custom-num" num="1"></div>

Key CSS and keyframes:

.custom-num { position:absolute; left:0; top:0; display:flex; width:2em; height:2em; font-size:2em; color:#fff; justify-content:center; align-items:center; margin-left:-1em; margin-top:-2em; font-weight:bold; text-shadow:4px 4px 0 rgba(255,0,0,0); transform: translate(var(--left), var(--top)); }
.custom-num::before { content:'+' attr(num); opacity:0; animation: count-shark 1s var(--d,0s); }
@keyframes count-shark {
  0%,100% { opacity:0; transform:scale(.4); }
  30%,70% { opacity:1; transform:scale(1); }
}

JavaScript increments the number by removing the old element and inserting a new one with an increased num attribute.

function createNum() {
  const current = document.querySelector('.custom-num');
  let num = 1;
  if (current) {
    num = parseInt(current.getAttribute('num')) + 1;
    current.remove();
  }
  const numDiv = document.createElement('div');
  numDiv.className = 'custom-num';
  numDiv.setAttribute('num', num);
  numDiv.addEventListener('animationend', () => numDiv.remove());
  return numDiv;
}

5. Canvas Alternative

For many particles, canvas offers better performance, though it may be less beginner‑friendly. The article mentions the Canvas Confetti library as a ready‑made solution for confetti‑style effects.

All the pieces combined reproduce the eye‑catching like‑button animation shown at the beginning of the article.

Particle effect demo
Particle effect demo
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

FrontendanimationJavaScriptCSSparticle effect
Full-Stack Cultivation Path
Written by

Full-Stack Cultivation Path

Focused on sharing practical tech content about TypeScript, Vue 3, front-end architecture, and source code analysis.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.