인터랙션

[컨페티 분출]의존성 0의 canvas 파티클

클릭한 자리에서 종이조각이 분출되는 효과. canvas 2D + RAF만으로 — 라이브러리 0, 5KB 이하.

Canvas 2DrequestAnimationFramePointer EventsTypeScriptDPR scaling
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약

가입 완료결제 성공처럼 사용자가 "해냈다" 순간 화면 어딘가에서 종이조각이 터지는 효과. canvas 위에서 RAF로 굴리는 파티클 시뮬레이션.

이럴 때 필요해요

성취 순간을 텍스트 한 줄로 끝내기엔 아쉬울 때. 외부 라이브러리(canvas-confetti 등)는 30KB 정도 되는데, 필요한 효과가 한 곳뿐이라면 인라인으로 직접 짜는 게 가볍습니다. 색중력입자 수를 우리 디자인에 맞춰 조절할 수 있고요.

어떻게 동작하나

1. 클릭 좌표에서 위쪽 부채꼴(0.9rad) 방향으로 N개 파티클을 만듭니다 각각 무작위 속도회전색.

2. RAF 루프에서 매 프레임 vxvy를 약간 감쇠시키고 vy에 중력을 더해 자연스럽게 떨어뜨립니다.

3. 파티클을 그릴 때 rotate로 종이조각의 회전을, globalAlpha로 수명에 따른 페이드를 표현. life 0 이거나 화면 밖이면 배열에서 제거.

놓치기 쉬운 것

- DPR(devicePixelRatio)을 무시하면 레티나에서 흐릿하게 보입니다. canvas 픽셀 크기는 DPR을 곱하고, ctx에 setTransform(dpr, 0, 0, dpr, 0, 0)로 보정.

- 파티클이 0이 되면 RAF를 멈춰야 합니다. 계속 돌면 빈 캔버스를 지우는 비용이 영원히 듭니다.

- prefers-reduced-motion을 존중하는 게 좋습니다 멀미간질 사용자 배려.

이런 곳에 써요

- 가입결제 완료 토스트 옆

- 챌린지퀘스트 달성 모달

- 친구 초대 성공 모먼트

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { ConfettiBurstDemo } from "./-components/ConfettiBurstDemo";

export const metadata: Metadata = {
  title: "컨페티 분출 (데모)",
  description:
    "클릭 지점에서 canvas 파티클로 컨페티가 분출. 의존성 0의 순수 canvas + requestAnimationFrame 구현.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main
      data-demo-embed-fixed="560"
      className="flex min-h-[100dvh] items-center justify-center bg-gradient-to-br from-slate-50 via-white to-violet-50/40"
    >
      <ConfettiBurstDemo />
    </main>
  );
}
26조회수

댓글