AD JB Toolbox 01

CSS 강좌 / @keyframes 애니메이션과 움직임 줄이기

@keyframes는 애니메이션의 여러 시점에서 사용할 CSS 값을 정의하고, animation은 그 변화를 언제·얼마나·어떤 방향으로 재생할지 정합니다. 사용자의 호버 없이 실행하거나 중간 상태와 반복이 필요한 효과에 적합합니다. 움직임은 내용을 읽거나 조작하는 데 방해되지 않아야 하며, 줄이기 설정에서도 필요한 정보를 바로 볼 수 있게 기본 상태를 작성합니다.

transition과 animation을 구분하기

기준 transition animation
시작 조건 호버·초점·클래스 변경 등으로 값이 바뀔 때 animation이 적용되고 지연 시간이 지나면
상태 정의 바뀌기 전과 후의 CSS 값 키프레임에서 시작·중간·끝 값 지정
반복과 방향 상태 변경을 따라 진행 횟수·방향·정지 상태를 별도 지정

버튼의 배경색이 바뀌는 정도라면 transition이 간단합니다. 알림이 처음 나타나는 효과나 여러 단계를 거치는 시각적 설명에는 animation을 검토할 수 있습니다. 같은 요소의 같은 속성을 두 기능으로 동시에 제어하면 어느 값이 적용되는지 추적하기 어려워지므로 이 실습에서는 역할을 나눕니다.

키프레임과 재생 설정 읽기

키프레임의 from은 0%, to는 100%에 해당합니다. 중간 상태가 필요하면 50%처럼 진행 비율을 추가합니다. 키프레임 이름만 선언해도 재생되지는 않으며 요소의 animation-name과 연결해야 합니다.

@keyframes travel {
  0% { transform: translateX(0); }
  50% { transform: translateX(3rem); }
  100% { transform: translateX(6rem); }
}
.marker { animation: travel 4s linear 1s 1 normal forwards; }

이 부분 코드는 아래 전체 예제에서 같은 이름의 키프레임과 .marker 설정을 짧게 표현한 것입니다. 4s는 한 번 재생하는 데 걸리는 시간이고 1s는 시작 지연입니다. 지속 시간을 지정하지 않으면 기본값 0초라서 움직이는 과정을 관찰하기 어렵습니다. 처음에는 긴 속성으로 나누어 작성하면 시간과 횟수를 혼동하는 일을 줄일 수 있습니다.

속성 이 예제의 값 바꾸어 볼 점
animation-duration 4s 한 번의 재생 시간
animation-timing-function linear 일정한 진행과 ease 곡선의 차이
animation-delay 1s 시작 전 대기 시간
animation-iteration-count 1 재생 횟수; infinite는 무한 반복
animation-direction normal 정방향·역방향·교대 재생
animation-fill-mode forwards 재생 밖 시간에 적용할 값
animation-play-state 기본값 running paused로 현재 진행을 일시 정지

비율이 같은 간격으로 적혀 있어도 실제 위치 변화는 시간 함수에 따라 달라집니다. 이번 실습은 중간 위치를 확인하기 쉬운 linear를 사용합니다. 키프레임 작성과 연결은 MDN @keyframes 문서를 참고하세요.

반복 방향과 마지막 상태를 정확히 이해하기

normal은 처음에서 끝으로, reverse는 끝에서 처음으로 진행합니다. alternate는 첫 회에 정방향, 다음 회에 역방향으로 번갈아 재생합니다. 반복 횟수가 2이고 alternate라면 마지막은 처음 키프레임에 도착합니다.

forwards는 항상 100% 키프레임을 유지하는 설정이 아닙니다. 재생 방향과 횟수에 따라 실제 마지막에 도달한 키프레임의 값을 유지합니다. 예제의 정방향 1회는 6rem 위치를, 역방향 1회는 0 위치를, alternate 2회도 0 위치를 유지합니다. 이 설명은 정수 횟수의 완료된 재생을 기준으로 하며, 소수 횟수는 중간 진행에서 끝날 수 있습니다. MDN fill-mode 설명에 방향별 조건이 정리되어 있습니다.

fill-mode 지연 중 종료 후
none 기본 CSS 기본 CSS
forwards 기본 CSS 마지막에 재생한 키프레임
backwards 처음 재생할 키프레임 기본 CSS
both 처음 재생할 키프레임 마지막에 재생한 키프레임

기본 CSS 값과 키프레임 끝 값이 같으면 none과 forwards의 차이가 눈에 보이지 않을 수 있습니다. 아래 이동 박스는 기본 위치를 1rem, 시작을 0, 끝을 6rem으로 다르게 두어 차이가 드러나게 만들었습니다. 반면 꼭 읽어야 하는 안내는 애니메이션을 제거했을 때 보이는 완성 상태를 기본값으로 둡니다.

전체 예제와 한 가지 설정씩 바꾸는 실습

다음 코드를 UTF-8의 animation-lab.html로 저장하고 브라우저로 엽니다. 위쪽 안내는 짧게 등장하고, 아래 장식 박스는 1초 뒤 이동을 시작해 4초 동안 움직입니다. 값 수정 후 파일을 저장하고 페이지를 새로고침하면 처음부터 비교할 수 있습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>키프레임과 종료 상태 실습</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
.notice { padding: 1rem; border: 1px solid #90bdd8; border-radius: .5rem; background: #eaf4fa;
  opacity: 1; transform: none; animation: appear 400ms ease-out; }
@keyframes appear {
  from { opacity: 0; transform: translateY(.5rem); }
  to { opacity: 1; transform: translateY(0); }
}
.track { margin-block: 1rem; padding: 1rem; border: 2px dashed #8a9ba7; }
.marker { width: 2rem; height: 2rem; border-radius: .25rem; background: #166b92;
  transform: translateX(1rem);
  animation-name: travel; animation-duration: 4s; animation-timing-function: linear;
  animation-delay: 1s; animation-iteration-count: 1; animation-direction: normal;
  animation-fill-mode: forwards; }
@keyframes travel {
  0% { transform: translateX(0); background-color: #166b92; }
  50% { transform: translateX(3rem); background-color: #6a3989; }
  100% { transform: translateX(6rem); background-color: #28633b; }
}
#pause:checked ~ .track .marker { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .notice, .marker { animation: none; }
}
  </style>
</head>
<body>
<main>
<h1>등장과 이동 확인</h1>
<p class="notice">이 안내는 애니메이션이 없어도 읽을 수 있습니다.</p>
<h2>종료 상태 비교</h2>
<p>이동은 불러온 뒤 1초 후에 시작합니다. 새로고침해 다시 비교하세요.</p>
<input id="pause" type="checkbox"><label for="pause">이동 일시 정지</label>
<div class="track"><div class="marker" aria-hidden="true"></div></div>
<p>색과 위치는 실습용 장식입니다. 중요한 내용은 이 문장처럼 움직임과 별도로 제공합니다.</p>
</main>
</body>
</html>
  1. 기본 설정에서는 박스가 지연 중 1rem 위치에 있다가 시작 시 0으로 가서 이동하고, 종료 후 6rem 위치에 남습니다. 지연 중 값과 시작 값의 차이를 확인합니다.
  2. animation-fill-mode만 none으로 바꾸고 새로고침합니다. 끝난 뒤 기본 위치 1rem과 기본 파란색으로 돌아갑니다. 다시 forwards로 복원합니다.
  3. fill-mode만 both로 바꿉니다. 지연 중에도 시작 위치 0이 적용됩니다. 확인 후 forwards로 되돌립니다.
  4. 횟수는 1로 두고 방향만 reverse로 바꿉니다. 종료 후 0 위치에 남는 것을 확인한 뒤 normal로 복원합니다.
  5. 방향을 alternate로 바꾸고 횟수는 1로 유지한 결과를 확인합니다. 그 상태에서 횟수만 2로 바꾸면 돌아오는 두 번째 재생이 생깁니다. 확인 후 횟수 1, 방향 normal로 복원합니다.
  6. 새로고침 직후 일시 정지 체크박스를 켜고 잠시 기다립니다. 해제하면 중단했던 위치부터 진행합니다. 이것은 처음부터 다시 재생하는 기능과 다릅니다.

움직임을 줄이고 내용은 유지하기

감소 설정에서는 안내와 장식 박스의 animation을 없앱니다. 안내는 기본값이 opacity: 1이므로 계속 읽을 수 있고, 박스는 기본 위치 1rem에 표시됩니다. 종료 키프레임이나 forwards에만 읽을 수 있는 상태를 맡기면 animation이 없을 때 내용이 사라질 수 있습니다.

개발자 도구의 Rendering 패널이나 운영체제에서 움직임 감소를 설정하고 새로고침하세요. 두 요소 모두 움직이지 않고 내용은 보여야 합니다. 설정 확인 방법은 MDN prefers-reduced-motion 안내를 참고하고, 비교를 마치면 재현 설정을 기본값으로 복원합니다.

무한 반복을 추가하기 전에 움직임의 목적과 멈출 방법을 판단하세요. 장식만을 위해 계속 움직이는 UI는 주의를 분산시킬 수 있습니다. 이 예제는 유한 횟수를 사용하며, 색이나 움직임만으로 중요한 정보를 전달하지 않습니다. 동작이 없을 때는 이름의 철자·지속 시간·감소 설정을 확인하고, 종료 후 값이 예상과 다를 때는 방향·횟수·fill-mode·기본 CSS를 순서대로 비교하면 원인을 좁힐 수 있습니다.

같은 카테고리의 다른 글
CSS 강좌 / 이미지와 동영상 크기·비율 맞추기

CSS 강좌 / 이미지와 동영상 크기·비율 맞추기

이미지와 동영상의 원본 비율과 표시 영역을 구분하고 aspect-ratio·object-fit으로 크기와 잘림을 조절합니다. 같은 그림의 네 가지 비교와 재생 가능한 영상 예제로 결과를 확인합니다.

CSS 강좌 / 세로 가운데 정렬하는 방법

CSS 강좌 / 세로 가운데 정렬하는 방법

CSS에서 텍스트의 가로 가운데 정렬은 text-align 속성을 이용합니다. 요소의 가로 가운데 정렬은 margin 속성을 이용합니다. 세로 정렬을 가운데로 하려면 어떻게 해야 할까요? 네 가지 방법을 소개합니다.

CSS 강좌 / !important

CSS 강좌 / !important

CSS에서 !important는 특정 스타일 규칙이 다른 모든 스타일 규칙보다 우선 적용되도록 강제하는 데 사용됩니다. 이 키워드는 일반적인 스타일 우선 순위를 무시하고, 해당 스타일이 다른 어떤 스타일보다도 높은 우선순위를 가지도록 합니다.

CSS 강좌 / 모바일·큰 화면·확대 보기에서 레이아웃 점검하기

CSS 강좌 / 모바일·큰 화면·확대 보기에서 레이아웃 점검하기

점검용 페이지에서 화면 폭, 확대, 긴 문자열과 키보드 조작을 확인합니다. 가로 넘침과 글자 잘림을 재현하고 원인 확인부터 수정·재점검까지 실습합니다.

CSS 강좌 / width, height, min, max, box-sizing으로 크기 조절하기

CSS 강좌 / width, height, min, max, box-sizing으로 크기 조절하기

content-box와 border-box의 너비 계산, 최소·최대 크기, 고정 높이와 내용에 맞는 높이를 비교합니다. 화면과 글자 크기를 바꾸며 넘침을 진단하고 백분율 높이의 조건을 확인합니다.

CSS 강좌 / 브라우저 개발자 도구로 CSS 확인하고 수정하기

CSS 강좌 / 브라우저 개발자 도구로 CSS 확인하고 수정하기

브라우저 개발자 도구에서 안내 문단을 선택하고, Styles의 선언과 Computed의 최종 값을 확인합니다. 색상 변경·선언 끄기·원본 저장 실습과 적용되지 않는 CSS의 원인별 점검 방법을 제공합니다.

CSS 강좌 / 표 꾸미기 / 배경색

CSS 강좌 / 표 꾸미기 / 배경색

배경색은 background-color 속성으로 만듭니다. table, tr, th, td, thead, tbody, tfoot에 적용할 수 있습니다. 다음 표를 기본으로 하고, 배경색을 여러 곳에 추가해보겠습니다.

CSS 강좌 / 관계 선택자와 가상 클래스로 요소 고르기

CSS 강좌 / 관계 선택자와 가상 클래스로 요소 고르기

자손·자식·인접 형제·일반 형제 선택자를 같은 문서 구조에서 비교합니다. hover·focus-visible·first-child·nth-child를 포함한 전체 예제와 구조를 바꿔 선택 결과를 확인하는 실습을 제공합니다.

CSS 강좌 / 링크 꾸미는 방법

CSS 강좌 / 링크 꾸미는 방법

링크의 기본 밑줄과 방문·호버·활성 상태를 설정하고 키보드 포커스를 표시합니다. 두 파일로 방문 이력과 Tab·Enter 동작을 확인하며 상태 규칙이 겹칠 때의 결과를 설명합니다.

CSS 강좌 / position의 relative·absolute·fixed·sticky 차이

CSS 강좌 / position의 relative·absolute·fixed·sticky 차이

relative·absolute·fixed·sticky의 공간 유지, 위치 기준과 스크롤 차이를 비교합니다. 배지와 고정 버튼, 붙는 제목 예제를 실행하며 기준 부모와 동작하지 않는 원인을 확인합니다.