GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

CSS 통통 튀는 애니메이션 9종 — 한 줄 복붙으로

CSS 통통 튀는 애니메이션은 요소를 그냥 나타나게 하지 않고 뜨고, 튕기고, 떨고, 흔들리게 만들어 살아 있는 느낌을 주는 움직임입니다. 흔한 영어 이름 대신 둥실둥실·포용·푸루푸루 같은 의성어로 아홉 개를 골랐고, 그중 두 개(포용·스르륵)는 실제로 마우스를 올려야 움직이는 진짜

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 이름 가나다순이 아니라 버튼 하나가 화면에서 밟는 순서로 놓았습니다. 존재를 알리고(01), 눌러 달라고 조르고(02~03), 옆에서 조용히 숨 쉬고(04~05), 더 보라고 손짓하고(06), 뒤에서 배경을 꾸미고(07), 잘 골랐다고 반짝이고(08), 다음 화면으로 미끄러지듯 넘어갑니다(09). 표에서 duration과 트리거만 먼저 훑고, 각 항목 섹션에서 실제 코드를 열어 보십시오. 통통 튀는 소리에 색종이까지 얹으면 컨페티 축하 애니메이션이 그 파티를 벌이고, 방향을 짚어 주는 작은 움직임이 필요하면 CSS 화살표 애니메이션이 길을 가리킵니다.

클래스 무엇이 움직이나 duration 트리거
.o-float translateY·rotate 2.8s 상시
.o-poyon scale 550ms 호버·포커스
.o-puru scaleX·scaleY 900ms 상시
.o-yura rotate 2.4s 상시
.o-kuru rotate 1.6s 상시
.o-pyon translateY·scaleY 900ms 상시
.o-gunya border-radius 3.2s 상시
.o-kira opacity·scale 1.8s 상시
.o-nuru translateX·scale 700ms 호버·포커스

01둥실둥실 뜨는(ふわふわ)

어떤 화면인가

.o-float 하나만 붙이면 배지가 translateY와 약한 rotate로 천천히 오르내립니다.

둥실둥실 뜨는(ふわふわ)
새 창에서 열기

01 · 둥실둥실 뜨는(ふわふわ) — 측정 누적면적 3.606% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateYrotate 3deg2.8s

동작 원리

신상품 배지나 프로모션 알림 칩처럼 화면 한구석에서 계속 존재를 알려야 하는 요소에 씁니다.

$duration: 2.8s;
$easing: ease-in-out;
$color: $subject-blue;

.o-float {
  animation: o-float $duration $easing infinite;
}

@keyframes o-float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-14px) rotate(-3deg); }
}

02포용 튕기는(ぽよん)

css 버튼 호버 애니메이션은 어디에 쓰나

.o-poyon은 마우스를 올리거나 키보드로 포커스하면 scale이 눌렸다 크게 튕기는 elastic 이징으로 한 번 움직입니다.

포용 튕기는(ぽよん)
새 창에서 열기

02 · 포용 튕기는(ぽよん) — 측정 누적면적 5.788% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scale overshootelastic550ms

만드는 방식

장바구니 담기나 좋아요처럼 누르고 싶게 만들어야 하는 버튼에 씁니다.

$duration: 550ms;
$easing: cubic-bezier(.34, 1.56, .64, 1);
$color: $subject-cream;

.btn.o-poyon:hover,
.btn.o-poyon:focus-visible { animation: o-poyon $duration $easing 1; }

@keyframes o-poyon {
  0%   { transform: scale(1, 1); }
  30%  { transform: scale(.84, 1.09); }
  55%  { transform: scale(1.20, .89); }
  75%  { transform: scale(.94, 1.03); }
  100% { transform: scale(1, 1); }
}

03푸루푸루 젤리(ぷるぷる)

어디에 쓰나

.o-puru는 가로·세로 scale을 아주 작은 폭으로 서로 어긋나게 반복합니다.

푸루푸루 젤리(ぷるぷる)
새 창에서 열기

03 · 푸루푸루 젤리(ぷるぷる) — 측정 누적면적 0.772% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scale(1.07, .94)어긋난 위상900ms

왜 이렇게 만들었나

찜하기 하트나 알림 종 아이콘이 탭해 달라는 듯 계속 미세하게 떠는 느낌을 냅니다.

$duration: 900ms;
$easing: ease-in-out;
$color: $subject-cream;

.o-puru {
  animation: o-puru $duration $easing infinite;
}

@keyframes o-puru {
  0%, 100% { transform: scale(1, 1); }
  25%      { transform: scale(1.07, .94); }
  50%      { transform: scale(1, 1.07); }
  75%      { transform: scale(.93, 1); }
}

04살랑살랑 흔들리는(ゆらゆら)

눈에 보이는 것

.o-yura는 transform-origin을 위쪽에 고정하고 rotate만 좌우로 아주 천천히 오갑니다.

살랑살랑 흔들리는(ゆらゆら)
새 창에서 열기

04 · 살랑살랑 흔들리는(ゆらゆら) — 측정 누적면적 1.628% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

rotatetransform-origin: top2.4s

구현의 핵심

매달린 세일 태그나 알림 종 장식이 바람에 살랑이듯 보이게 할 때 씁니다.

$duration: 2.4s;
$easing: ease-in-out;
$color: $subject-blue;

.o-yura {
  animation: o-yura $duration $easing infinite;
  transform-origin: top center;
}

@keyframes o-yura {
  0%, 100% { transform: rotate(-6deg); }
  50%      { transform: rotate(6deg); }
}

05빙글빙글 도는(くるくる)

어떤 상황인가

.o-kuru는 rotate 360도를 한 번에 linear로 밀지 않고 구간마다 통과 지점을 다르게 둬 한 박자 튕기는 듯한 리듬을 만듭니다. 새로고침이나 동기화 아이콘에 씁니다.

빙글빙글 도는(くるくる)
새 창에서 열기

05 · 빙글빙글 도는(くるくる) — 측정 누적면적 0.403% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

rotate 360deg구간별 easing1.6s

무엇이 핵심인가

$duration: 1.6s;
$easing: cubic-bezier(.65, 0, .35, 1);
$color: $subject-cream;

.o-kuru {
  animation: o-kuru $duration $easing infinite;
}

@keyframes o-kuru {
  0%   { transform: rotate(0deg); }
  55%  { transform: rotate(230deg); }
  80%  { transform: rotate(330deg); }
  100% { transform: rotate(360deg); }
}

06깡충깡충 뛰는(ぴょんぴょん)

무슨 일이 일어나나

.o-pyon은 착지 직전엔 세로 scale을 눌러 납작하게, 뛰어오르는 순간엔 translateY와 scale을 늘려 통통 뛰는 리듬을 만듭니다.

깡충깡충 뛰는(ぴょんぴょん)
새 창에서 열기

06 · 깡충깡충 뛰는(ぴょんぴょん) — 측정 누적면적 5.925% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateY+scale착지 스쿼시900ms

짚어 둘 점

"더 보기" 화살표나 스크롤 유도 아이콘에 씁니다.

$duration: 900ms;
$easing: ease-in-out;
$color: $subject-blue;

.o-pyon {
  animation: o-pyon $duration $easing infinite;
}

@keyframes o-pyon {
  0%, 100% { transform: translateY(0)     scale(1, 1); }
  10%      { transform: translateY(2px)   scale(1.11, .9); }
  40%      { transform: translateY(-16px) scale(.93, 1.08); }
  70%      { transform: translateY(2px)   scale(1.11, .9); }
  85%      { transform: translateY(0)     scale(1.02, 1.03); }
}

07말랑말랑 휘는(ぐにゃぐにゃ)

화면에서 벌어지는 일

.o-gunya는 border-radius 네 모서리 값을 각각 다른 타이밍으로 keyframes 안에서 바꿔 배경 블롭 도형이 말랑하게 형태를 바꾸며 떠다니게 합니다.

말랑말랑 휘는(ぐにゃぐにゃ)
새 창에서 열기

07 · 말랑말랑 휘는(ぐにゃぐにゃ) — 측정 누적면적 1.768% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

border-radius 4분할비대칭 keyframes3.2s

어떻게 만들었나

히어로 배경이나 기능 카드 뒤 악센트 도형에 씁니다.

$duration: 3.2s;
$easing: ease-in-out;
$color: $subject-cream;

.o-gunya {
  animation: o-gunya $duration $easing infinite;
}

@keyframes o-gunya {
  0%, 100% { border-radius: 50% 63% 68% 63% / 50% 37% 32% 37%; transform: rotate(0deg); }
  20%      { border-radius: 67% 66% 56% 42% / 33% 34% 44% 58%; transform: rotate(5deg); }
  45%      { border-radius: 56% 42% 33% 34% / 44% 58% 67% 66%; transform: rotate(2deg); }
  70%      { border-radius: 33% 34% 44% 58% / 67% 66% 56% 42%; transform: rotate(-5deg); }
  95%      { border-radius: 44% 58% 67% 66% / 56% 42% 33% 34%; transform: rotate(-2deg); }
}

08반짝(キラッ)

어떻게 보이나

.o-kira는 ::before·::after로 만든 작은 별 두 개에 서로 다른 delay를 줘 scale·opacity로 번갈아 반짝이게 합니다.

반짝(キラッ)
새 창에서 열기

08 · 반짝(キラッ) — 측정 누적면적 0.399% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scale+opacity::before/::after1.8s

코드 읽는 법

추천 배지나 신규 기능 표시가 주기적으로 반짝이며 눈길을 끌게 합니다.

$duration: 1.8s;
$easing: ease-in-out;
$color: $subject-blue;

.badge.o-kira::before { animation: o-kira $duration $easing infinite; }
.badge.o-kira::after  { animation: o-kira $duration $easing infinite; animation-delay: .4s; }

@keyframes o-kira {
  0%, 45%, 100% { opacity: 0; transform: scale(.4); }
  62%           { opacity: 1; transform: scale(1.3); }
  78%           { opacity: 0; transform: scale(.6); }
}

09스르륵 매끄러운(ぬるっと)

언제 쓰나

.o-nuru는 마우스를 올리거나 포커스하면 화살표가 ease-out-expo에 가까운 cubic-bezier로 미끄러지듯 이동합니다.

스르륵 매끄러운(ぬるっと)
새 창에서 열기

09 · 스르륵 매끄러운(ぬるっと) — 측정 누적면적 0.172% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateXcubic-bezier(.16, 1, .3, 1)700ms

코드의 요점

다른 여덟 개의 탄성과 대비되는 매끈한 느낌이라 "자세히 보기" 링크에 어울립니다.

$duration: 700ms;
$easing: cubic-bezier(.16, 1, .3, 1);
$color: $subject-blue;

.link.o-nuru:hover .link__arrow,
.link.o-nuru:focus-visible .link__arrow { animation: o-nuru $duration $easing 1; }

@keyframes o-nuru {
  0%   { transform: translateX(0)    scale(1); }
  100% { transform: translateX(28px) scale(1.15); }
}

어디서 깨지는가 — is-demo 클래스를 안 지우면 생기는 함정

무엇이 문제인가

css 버튼 클릭 애니메이션이 아니라 호버로 트리거되는 02와 09 같은 항목은 실제 동작용 규칙(:hover·:focus-visible)과 이 페이지를 자동 재생시키는 .is-demo 루프용 규칙을 같은 요소에 함께 둡니다. 두 규칙은 특정성이 똑같아서(클래스 두세 개, 동점) 나중에 선언된 쪽이 이깁니다. 그래서 .is-demo를 진짜 화면에 붙인 채로 배포하면, 사용자가 마우스를 올려도 짧은 트리거 애니메이션 대신 2초짜리 데모 루프가 재생됩니다. zip을 코드에 붙여 쓸 때는 is-demo 클래스만 지우면 되고, 반대로 is-demo를 남겨 두고 싶다면 실사용 규칙을 스타일시트 더 아래에 적어야 합니다.

어떻게 피하나

물결 임계값을 넘겼을 때 흐릿해지는 문제는 손그림 질감을 살리는 크레용 랜딩 키트에서도 스케치 선이 뭉개지지 않게 같은 배율 규칙으로 막습니다.

아홉 개 중 07 말랑말랑 휘는 항목의 평균 강도(강도_평균)는 42.9으로 가장 낮게 측정됐는데, border-radius만으로 만든 곡선이 배경과의 대비가 완만해서입니다. 대비가 약한 배경 위에 올리면 거의 안 보일 수 있으니 배경과 색 차이가 뚜렷한 곳에 둬야 합니다. duration·easing·색을 전부 맞춰 둔 vanilla·React 파일 아홉 벌을 묶은 zip은 압축 비밀번호 kxj6u9k6로 열립니다.

접근성

reduced-motion 에서 남는 것

운영체제에 동작 줄이기 설정이 켜져 있다면 아홉 개 모두 @media (prefers-reduced-motion: reduce) 블록이 animation: none으로 움직임을 지웁니다. 01 둥실둥실·04 살랑살랑·07 말랑말랑처럼 배경을 꾸미기만 하는 동작은 지워도 잃는 정보가 없고, 02 포용·09 스르륵은 애니메이션이 빠져도 버튼 클릭이나 링크 이동 같은 실제 기능은 그대로 살아 있습니다. 이 미디어 쿼리를 지원하는 브라우저 목록은 MDN의 prefers-reduced-motion 문서 아래쪽 호환성 표에서 확인할 수 있습니다.

@media (prefers-reduced-motion: reduce) {
  .o-float, .o-puru, .o-yura,
  .o-kuru, .o-pyon, .o-gunya { animation: none; }
  .badge.o-kira::before, .badge.o-kira::after { animation: none; }
  .btn.o-poyon:hover, .btn.o-poyon:focus-visible,
  .link.o-nuru:hover .link__arrow,
  .link.o-nuru:focus-visible .link__arrow { animation: none; }
}

이어서 볼 글

이 시리즈의 다른 묶음은 CSS 애니메이션 카테고리에서 모아 보고, 이 사이트를 처음 본다면 소개 페이지부터 훑어보십시오.

FAQ

css 애니메이션 무한 반복을 아홉 개나 한 화면에 다 써도 됩니까?

기술적으로는 가능하지만 한 화면에 두세 개까지만 권합니다. 02 포용과 08 반짝은 똑같이 "여기를 봐 달라"고 조르는 역할이라 한 화면에 같이 두면 시선이 갈라집니다. 반대로 01 둥실둥실처럼 조용한 것과 05 빙글빙글처럼 상태를 알리는 것은 역할이 겹치지 않아 같이 둬도 됩니다.

왜 데모 화면에서는 02와 09가 계속 움직입니까?

실제 화면에서 02와 09는 마우스를 올리거나 포커스할 때만 움직이는 호버 트리거입니다. 이 페이지의 데모는 사람이 손을 대지 않아도 움직임을 보여줘야 해서 .is-demo 클래스로 2초마다 스스로 재생되게 강제한 것이고, 실제 코드에 붙일 때는 이 클래스를 지우면 됩니다.

React에서는 호버 트리거를 어떻게 재현합니까?

zip의 react/ 폴더는 duration·easing·color를 props로 받아 CSS 변수로 넘기고, 02·09처럼 호버로 트리거되는 항목도 상태 없이 CSS 모듈의 :hover·:focus-visible 선택자로 그대로 처리합니다. 01·03~08처럼 상시 반복되는 항목은 컴포넌트가 마운트되는 순간부터 애니메이션이 돕니다. 누르는 순간의 질감을 버튼에 그대로 얹고 싶다면 CSS 버튼 애니메이션이 그 첫 보기입니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.