Skip to content
This page has been auto-translated and may contain errors.View in English

트랜지션과 애니메이션

docs.scrimba.com

버튼에 :hover 색상을 추가하면 작동하지만 변화가 급작스럽습니다. 한 프레임에는 파란색이고 다음 프레임에는 더 어두운데, 그 사이에는 아무것도 없습니다. 작은 페이드만 있어도 전체 페이지가 덜 어색해집니다. 그 페이드가 트랜지션이며, 두 상태 사이의 모션을 설명할 수 있으면 호버 효과, 떠오르는 카드, 무언가가 로딩되는 동안 회전하는 스피너까지 같은 도구가 확대됩니다. 이 장에서는 CSS가 어떻게 움직이는지, 그리고 페이지를 사용하기 어렵게 만들지 않으면서 어떻게 움직일지 배우게 됩니다.

트랜지션

**트랜지션**은 브라우저에게 속성을 이전 값에서 새 값으로 즉시 변경하는 대신 애니메이션하도록 지시합니다. 두 상태, 즉 일반 상태와 호버 상태를 설명하면 트랜지션이 그 사이의 프레임을 채웁니다.

호버 시 색상이 변하는 버튼입니다. 트랜지션 없으면 색상이 점프하고, 있으면 페이드됩니다:

css
.btn {
  background: #3b82f6;
  transition: background 0.2s;   /* 0.2초 동안 색상을 페이드 */
}
.btn:hover {
  background: #1d4ed8;           /* 애니메이션할 새로운 상태 */
}

transition:hover이 아니라 일반 상태에 넣습니다. 이렇게 하면 마우스가 들어올 때와 나갈 때 모두 적용됩니다.

**트랜지션**은 속성 값이 변경될 때마다 그 속성을 두 값 사이에서 애니메이션합니다. 보통 :hover:focus 같은 상태가 시작되거나 클래스가 추가될 때입니다. 애니메이션을 프레임 단위로 설명하지 않습니다. 속성과 지속 시간을 이름 지으면 브라우저가 그 사이의 값들을 보간합니다.

transition 단축 속성은 네 부분을 가지며, 이 순서입니다: 감시할 속성, 지속 시간, 타이밍 함수(속도 곡선), 그리고 선택적 지연입니다.

css
.btn {
  background: #3b82f6;
  transition: background 0.2s ease-in-out 0s;
  /* 속성 | 지속시간 | 타이밍함수 | 지연 */
}
.btn:hover {
  background: #1d4ed8;
}

쉼표로 구분하여 여러 속성을 한 번에 트랜지션할 수 있으며, 기본 상태에 트랜지션을 선언하면 호버를 들어올 때와 나갈 때 모두 적용됩니다.

css
.card {
  transition: transform 0.2s, box-shadow 0.2s;   /* 두 속성, 같은 타이밍 */
}

**트랜지션**은 계산된 값이 변경될 때마다 시간 경과에 따라 속성을 시작 값과 최종 값 사이에서 보간합니다. 트리거는 그 속성에 대한 모든 변경입니다: :hover처럼 의사 클래스, JavaScript에서 전환된 클래스, 또는 인라인 스타일 편집. 네 개의 하위 속성은 transition-property, transition-duration, transition-timing-function, 그리고 transition-delay이며, 단축 속성은 속성, 지속 시간, 타이밍, 지연 순서로 배열합니다.

트랜지션이 실행되는지 여부를 결정하는 두 가지 제약이 있습니다. 속성이 애니메이션 가능해야 하는데, 이는 두 값 사이에 정의된 보간을 의미합니다: background-color, opacity, transform은 애니메이션 가능하지만 display 같은 속성은 이산적이며 역사적으로 트위닝보다는 즉시 변경됩니다. 그리고 시작 값과 최종 값이 모두 구체적이어야 합니다: auto로 트랜지션할 수 없으므로 heightauto로 애니메이션하는 것은 작동하지 않으며 사람들은 대신 max-heighttransform을 사용합니다.

css
.btn {
  background: #3b82f6;
  transition: background-color 0.2s ease-in-out;   /* 애니메이션 가능, 두 값 모두 구체적 */
}
.btn:hover {
  background: #1d4ed8;
}

:hover 규칙이 아니라 기본 규칙에 트랜지션을 선언합니다. 실행되는 트랜지션 속성은 요소의 현재 상태에서 읽히므로, 기본 상태에 놓으면 도착과 귀환을 모두 지배합니다. :hover에만 놓으면 들어올 때만 애니메이션하고 나갈 때는 즉시 변경됩니다.

JunoTransitions 트랜지션은 변화를 즉시 변경하는 대신 매끄럽게 합니다. 호버 시 버튼의 색상을 페이드 같은 것입니다. :hover이 아니라 일반 상태에 작성하면 들어올 때와 나갈 때 모두 작동합니다. propertyduration으로 시작하면 이미 작동하는 페이드가 있습니다.
JunoTransitionstransition 단축 속성은 속성, 지속 시간, 타이밍 함수, 지연을 읽으며, 쉼표로 구분하여 여러 개를 나열합니다. 기본 규칙에 넣으면 상태를 들어오고 나갈 때 모두 처리됩니다. 호버가 들어올 때는 매끄럽고 나갈 때는 점프하면, 트랜지션이 :hover에 있고 기본에 없는 것입니다. 이것은 자주 사람들을 잡습니다.
JunoTransitions 트랜지션은 두 개의 구체적인 최종 값을 가진 애니메이션 가능한 속성에서만 실행되므로, height: auto로 트랜지션하는 것이 조용히 아무것도 하지 않습니다. 귀환할 때 즉시 변경되는 대신 두 방향 모두를 지배하도록 기본 상태에 선언을 유지합니다. 네 개의 하위 속성을 알면, 단축 속성은 단지 그들의 순서입니다: 속성, 지속 시간, 타이밍, 지연.

변환

**변환(transform)**은 요소를 주변 요소를 방해하지 않으면서 이동, 크기 조정 또는 회전시킵니다. 이것은 일반적으로 애니메이션하고 싶은 것입니다. 매끄럽게 보이고 이웃을 밀어내지 않기 때문입니다.

계속 사용할 세 가지가 있습니다:

css
.card:hover {
  transform: translateY(-4px);   /* 위로 4픽셀 이동 */
}
.icon:hover {
  transform: scale(1.1);         /* 크기의 110%로 확대 */
}
.arrow.open {
  transform: rotate(90deg);      /* 시계 방향으로 1/4 회전 */
}

translate는 이동하고, scale은 크기 조정하고, rotate는 회전합니다. 이들 중 하나를 트랜지션과 결합하면 떠오르는 카드, 팝하는 아이콘, 또는 스윙 열리는 화살표를 얻습니다.

transform은 요소를 이동, 크기 조정 또는 회전하여 시각적 변화를 적용하되, 레이아웃에서 그 위치를 변경하지 않습니다. 마지막 부분이 중요합니다: 변환된 요소는 새로운 위치에 칠해지지만, 원래 차지하던 공간은 예약되어 있으므로 이웃이 재배치되지 않습니다. 이것이 top이나 margin 대신 transform으로 움직임을 애니메이션하는 이유입니다.

가장 자주 사용하는 세 가지는 translate(), scale(), 그리고 rotate()이며, 한 선언에서 이들을 결합할 수 있습니다:

css
.card {
  transition: transform 0.2s ease-out;
}
.card:hover {
  transform: translateY(-4px) scale(1.02);   /* 들어올리고 약간 확대 */
}

함수는 왼쪽에서 오른쪽으로 적용되며, transform-origin은 그들이 중심을 이루는 지점을 설정하는데, 기본값은 요소의 중심입니다. 이를 top left로 변경하면 rotate는 모서리에서 스윙됩니다.

css
.badge {
  transform-origin: top left;
  transform: rotate(-8deg);
}

transform은 요소를 자신의 칠해진 레이어 내에서 재배치하며 문서 흐름에 영향을 주지 않습니다: 상자의 원래 공간은 예약되어 있으므로 형제 요소가 재배치되지 않으며 트랜지션이 변경될 때 레이아웃이 재계산되지 않습니다. 그 속성이 transform을 애니메이션하기에 올바른 것을 만드는 것이며, 아래의 성능에 관한 Deep Dive에서 그 이유를 정확히 설명합니다.

translate(), scale(), rotate()는 공통 함수이며, 순서가 중요합니다. 왜냐하면 효과적으로 오른쪽에서 왼쪽으로 적용되는 행렬 곱셈으로 구성되기 때문입니다: translateX(50px) rotate(45deg)는 이동한 후 이동한 요소를 회전시키는 반면, 반대는 먼저 회전한 후 회전한 축을 따라 변환하여 다른 결과를 제공합니다. transform-originscalerotate의 중심점을 설정하며, 기본값은 요소의 중심입니다(50% 50%).

css
.panel {
  transform-origin: top center;
  transition: transform 0.25s ease-out;
}
.panel.collapsed {
  transform: scaleY(0);          /* 위쪽 가장자리 방향으로 축소 */
}

한 가지 실용적 제약이 있습니다: transform은 이렇게 위치할 수 있는 상자를 생성하는 요소에만 적용되므로, 맨 <span> 같은 인라인 요소에는 효과가 없습니다. 요소에 먼저 display: inline-block 또는 block을 지정합니다. 요소를 위치하기도 하면, 변환이 어떻게 위치 지정 컨텍스트와 상호작용하는지 주목합니다. 변환된 요소는 절대적으로 위치한 자손들을 위한 포함 블록이 되기 때문입니다.

JunoTransformstransform은 이웃을 밀어내지 않으면서 요소를 이동, 크기 조정, 또는 회전합니다. 기억해야 할 세 가지는 translate로 이동, scale로 크기 조정, rotate로 회전입니다. 트랜지션과 결합하면 호버 시 떠오르는 카드나 팝하는 아이콘을 얻습니다.
JunoTransformstransform은 레이아웃에서 요소가 앉아 있는 위치를 변경하지 않으므로 이웃이 결코 재배치되지 않습니다. 이것이 top이나 margin 대신 애니메이션하는 이유입니다. 한 선언에 translate, scale, rotate를 쌓으면 왼쪽에서 오른쪽으로 적용됩니다. 중심이 아닌 다른 곳에서 회전이나 크기 조정이 중심을 이루기를 원할 때 transform-origin을 사용합니다.
JunoTransforms 변환 함수는 순서대로 구성되므로 translateX(50px) rotate(45deg)는 반대와 같지 않으며, transform-origin은 회전이나 크기 조정의 중심을 설정합니다. 맨 인라인 요소에서는 inline-block으로 만들 때까지 아무것도 하지 않습니다. 또한 요소를 절대적으로 위치한 자식들을 위한 포함 블록으로 만들기도 하므로, 나중에 당신을 놀라게 할 수 있습니다.

키프레임 애니메이션

트랜지션은 두 상태 사이에 애니메이션합니다. 두 개 이상이 필요하거나 모션이 반복되어야 하면, **키프레임 애니메이션**을 사용합니다. @keyframes로 단계를 작성한 후 animation 속성으로 요소에 첨부합니다.

로딩 스피너는 고전적인 예입니다: 계속 회전하므로 트랜지션은 할 수 없습니다.

css
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.spinner {
  animation: spin 1s linear infinite;   /* spin 실행, 1초 회전, 무한 반복 */
}

from은 시작이고 to는 끝입니다. animation 줄은 실행할 키프레임, 한 사이클이 걸리는 시간, 속도 곡선, 그리고 반복 횟수를 말합니다. infinite는 절대 멈추지 않는다는 뜻입니다.

@keyframes는 명명된 단계 순서를 정의하고, animation 속성은 요소에서 이를 실행합니다. 트랜지션과 달리, 애니메이션은 실행될 상태 변경이 필요하지 않으며, 자동으로 시작되고, 0%에서 100%까지의 백분율을 사용하여 원하는 만큼 많은 단계를 가질 수 있습니다.

css
@keyframes pulse {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.05); }   /* 중간 단계 */
  100% { transform: scale(1); }
}
.badge {
  animation: pulse 1.5s ease-in-out infinite;
}

animation 단축 속성은 여러 하위 속성을 묶습니다: 이름, 지속 시간, 타이밍 함수, 지연, 반복 횟수, 그리고 방향입니다. infinite는 무한 반복하고, 숫자는 그 횟수를 실행하며, 방향으로서의 alternate는 각 사이클을 역방향으로 만들어 모션이 처음부터 점프하는 대신 앞뒤로 실행됩니다.

css
.spinner {
  animation: spin 1s linear infinite;
}

@keyframes는 백분율로 표현된 지점에서 속성 값의 명명된 타임라인을 선언합니다(fromto0%100%의 별칭입니다). animation은 그 타임라인을 요소에 바인딩합니다. 애니메이션은 자체 트리거이며 반복될 수 있으므로, 이것이 트랜지션과의 경계입니다: 상태 변경을 매끄럽게 하려면 트랜지션을 사용하고, 자체적으로 실행되거나 반복되는 모션에는 애니메이션을 사용합니다.

animation 단축 속성은 8개의 하위 속성을 포함합니다: animation-name, -duration, -timing-function, -delay, -iteration-count, -direction, -fill-mode, -play-state. 그 중 둘이 사람들을 잡습니다. animation-fill-mode: forwards는 애니메이션 후 요소가 최종 키프레임을 유지하도록 하며, 애니메이션 스타일로 다시 스냅하는 대신 이는 일회용 입장에 원하는 것입니다. 그리고 타이밍 함수는 전체 애니메이션이 아닌 키프레임 세그먼트마다 적용되므로, 3단계 애니메이션의 ease-in-out은 각 세그먼트의 끝에서 끝까지 한 번이 아니라 들어오고 나갑니다.

css
@keyframes slide-in {
  from { opacity: 0; transform: translateX(-16px); }
  to   { opacity: 1; transform: translateX(0); }
}
.toast {
  animation: slide-in 0.3s ease-out forwards;   /* 최종 상태 유지 */
}

가능한 한 키프레임 내에서 transformopacity를 애니메이션하는 것을 선호합니다. 트랜지션이 하는 이유와 같으며, 다음 섹션에서 풉니다: 이 두 속성은 애니메이션하기에 저렴하고 대부분 다른 속성은 그렇지 않습니다.

JunoKeyframe animations 두 상태가 충분하지 않으면, 또는 모션이 반복되어야 하면, @keyframes에 단계를 작성하고 animation으로 실행합니다. 스피너는 표준 예입니다: 0도에서 from, 360에서 to, infinite로 반복. animation 줄은 어느 키프레임, 얼마나 오래, 곡선, 그리고 몇 번을 설정합니다.
JunoKeyframe animations@keyframes는 백분율로 단계를 유지하고 animation은 이를 실행합니다. 상태 변경이 필요하지 않습니다. 단축 속성은 이름, 지속 시간, 타이밍, 지연, 반복 횟수, 방향을 포함하므로 infinite는 반복하고 alternate는 각 사이클이 앞뒤로 실행됩니다. 다중 단계 모션과 반복되는 모든 것은 여기에 속하며, 트랜지션에는 속하지 않습니다.
JunoKeyframe animations 타이밍 함수는 전체 애니메이션이 아닌 키프레임 세그먼트마다 실행되므로, 첫 번째로 이완된 다중 단계 실행이 덜거덕거려 보일 때 사람들을 놀라게 합니다. 일회용 입장이 스냅하는 대신 최종 상태를 유지해야 할 때 animation-fill-mode: forwards를 사용합니다. 그리고 가능한 한 키프레임을 transformopacity에 유지합니다. 다음 성능 이유 때문입니다.

사용자를 존중하는 모션

모든 사람이 화면의 모션을 원하는 것은 아닙니다. 어떤 사람들은 그것이 산만하다고 생각하고, 어떤 사람들에게는 실제로 불편함이나 구역질을 유발할 수 있습니다. 브라우저는 사람들이 시스템 설정에서 모션을 줄여달라고 요청할 수 있도록 하며, CSS는 **prefers-reduced-motion**으로 그 요청을 읽을 수 있습니다.

모션이 모션을 줄이도록 요청하지 않은 사람들을 위해서만 실행되도록 애니메이션을 확인으로 래핑합니다:

css
@media (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 0.2s;
  }
}

사용자를 존중하는 것의 다른 절반은 절제입니다: 모션을 짧고, 미묘하고, 호버나 클릭 같은 독자가 한 것과 연결된 것으로 유지합니다. 실제 변화에 주의를 끄는 모션은 도움이 됩니다; 자체 목적을 위한 모션은 빨리 지루해집니다.

모션은 중립적이지 않습니다. 어떤 독자들에게는 애니메이션이 산만하며, 전정 질환이 있는 사람들에게는 크거나 갑작스러운 모션이 현기증이나 구역질을 유발할 수 있습니다. 운영 체제는 "모션 감소" 설정을 노출하며, prefers-reduced-motion 미디어 기능을 사용하면 CSS가 이를 존중할 수 있습니다.

모션을 옵트인(opt-in)으로 처리하되, 옵트아웃(opt-out)이 아닙니다. 조용한 기본선을 기본값으로 작성한 후, no-preference 쿼리 내에 모션을 계층화합니다:

css
.card {
  transition: none;              /* 모든 사람을 위한 조용한 기본값 */
}
@media (prefers-reduced-motion: no-preference) {
  .card {
    transition: transform 0.2s ease-out;
  }
}

미디어 쿼리 외에도, 목적이 좋은 모션을 소음과 구분합니다. 모션이 변화를 설명할 때 자신의 자리를 얻습니다: 그것이 고정된 가장자리에서 미끄러지는 메뉴, 어디서나 나타나지 않도록 페이드되는 토스트. 빠르고 미묘한 것은 연마로 읽고; 느리고 큰 것은 장애물로 읽습니다.

prefers-reduced-motion은 독자가 OS 수준에서 선택한 접근성 설정을 반영하는 미디어 기능이며, 이를 존중하는 것은 좋음이 아니라 기본 요구 사항입니다. 원치 않는 모션이 전정 불편함을 유발할 수 있기 때문입니다. 도달할 패턴은 축소된 상태를 기본값으로 작성하고 @media (prefers-reduced-motion: reduce) 내에서 모션을 활성화하는 것입니다. 그렇게 하면 기능을 지원하지 않는 브라우저를 가진 독자도 여전히 모션이 아닌 조용한 기본선을 얻습니다.

"감소"는 "제거"를 거의 의미하지 않습니다. 페이드나 색상 변경은 보통 슬라이드나 스케일이 아닐 때 괜찮으므로, 유용한 이동은 모든 피드백을 죽이기보다는 큰 위치 모션을 작은 불투명도 변경으로 교환하는 것입니다:

css
.modal {
  animation: slide-in 0.3s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .modal {
    animation: fade-in 0.3s ease-out;   /* 피드백 유지, 움직임 제거 */
  }
}

목적과 이완은 나머지를 처리합니다. 입장은 ease-out으로 가장 잘 읽히는데, 빠르게 시작하고 정착하므로 요소가 의도를 가지고 도착하고 쉬러 옵니다; 퇴장은 종종 ease-in에 맞습니다. 지속 시간을 짧게 유지합니다(대략 150ms에서 300ms 사이의 인터페이스 피드백). 모션을 작게 유지합니다; 일반적인 실패는 모든 상호 작용에 1초의 대기를 추가하는 멋진 애니메이션입니다. 모션은 상태 변화를 장식하는 것이 아니라 명확히 해야 합니다.

JunoMotion that respects the user 어떤 사람들은 모션을 산만하다고 생각하거나 심지어 신체적으로 불편합니다. 그들은 브라우저에서 그것을 덜 받아달라고 요청할 수 있습니다. prefers-reduced-motion으로 읽으면 모션을 줄이도록 요청하지 않은 사람들을 위해서만 애니메이션을 실행합니다. 당신이 추가하는 모션을 짧고, 미묘하고, 독자가 한 것과 연결되게 유지합니다.
JunoMotion that respects the user 조용한 버전을 기본값으로 작성하고 no-preference 쿼리 내에 모션을 추가합니다. 그래서 모션은 사람들이 싸워야 하는 것이 아니라 옵트인입니다. 좋은 모션은 변화를 설명합니다. 메뉴가 고정점에서 미끄러지는 것처럼; 장식을 위한 모션은 빨리 지루해집니다. 빠르고 작은 것은 연마로 읽고, 느리고 큰 것은 장애물로 읽습니다.
JunoMotion that respects the user 감소는 거의 제거를 의미하지 않습니다: 슬라이드를 페이드로 교환하여 모션을 줄이도록 요청한 사람들이 여전히 피드백을 받습니다. 그리고 지원하지 않는 브라우저가 거기에도 도착하도록 조용한 상태를 기본값으로 지정합니다. 입장에 ease-out을 사용하고 인터페이스 피드백을 150에서 300 밀리초 사이로 유지합니다. 모든 클릭에 대기를 추가하는 아름다운 애니메이션은 기능이 아니라 비용입니다.

무엇을 애니메이션할지, 그리고 이유

나중에 당신을 괴롭히는 것을 막을 빠른 규칙: 움직임을 애니메이션할 때, width, height, top, margin 같은 속성이 아니라 transformopacity를 애니메이션합니다.

css
/* 매끄러운: 브라우저가 이들을 저렴하게 처리합니다 */
.card:hover {
  transform: translateY(-4px);
  opacity: 0.9;
}

이유는 transformopacity가 브라우저가 가장 저렴하게 움직일 수 있는 두 속성이므로, 그들에 기초한 애니메이션이 느린 장치에서도 매끄럽게 유지된다는 것입니다. 크기 또는 위치 속성을 애니메이션하면 브라우저가 매 프레임마다 더 많은 작업을 하도록 강제하며, 모션이 덜거덕거릴 수 있습니다. 당신은 더 깊이 들어가면 완전한 이유를 만날 것이지만, 지금 습관을 구축할 가치가 있습니다.

transformopacity에 기울어지는 이 장의 예들이 있는 이유가 있습니다. 이 두 속성은 애니메이션하기에 가장 저렴하고 대부분 다른 것들은 그렇지 않습니다. width, height, top, margin을 애니메이션하면 브라우저가 매 프레임마다 레이아웃을 재계산하도록 강제하며, 이는 비싸고 느린 장치에서 덜거덕거림으로 나타납니다.

실용적 규칙은 가능한 한 transformopacity로 모션을 표현하는 것입니다. 뭔가를 움직이고 싶으세요? top이 아닌 translate를 사용합니다. 크기를 조정하고 싶으세요? width가 아닌 scale를 사용합니다. 나타나길 원하세요? height를 0에서 애니메이션하지 말고 opacity를 페이드합니다.

css
/* 비싼: 레이아웃 속성 애니메이션 */
.panel {
  transition: height 0.3s;       /* 매 프레임마다 레이아웃 재계산 */
}

/* 저렴한: 같은 효과, 변환으로 표현 */
.panel {
  transition: transform 0.3s;
  transform: scaleY(0);          /* 레이아웃 작업 없음 */
}

이것이 transform이 이웃을 방해하지 않는 이유로 연결됩니다: 레이아웃을 절대 건드리지 않기 때문에 브라우저가 페이지를 다시 하지 않고 애니메이션할 수 있습니다. 당신이 또한 색상이나 배경 트랜지션에 도달한다면, 색상과 배경은 그 중 어느 것이 보간되는지 다룹니다.

이것이 Deep Dive입니다. 이전 섹션들이 지적한 것입니다: 왜 transformopacity는 애니메이션하기에 저렴하고 거의 다른 모든 것은 그렇지 않습니다. 브라우저의 렌더링 파이프라인으로 내려오며, 3개 단계에서 실행됩니다. 레이아웃은 모든 상자의 크기와 위치를 계산합니다. 칠하기는 픽셀을 채웁니다: 색상, 텍스트, 테두리. 합성은 칠해진 레이어들을 최종 프레임으로 조립하며, 이것이 GPU가 구동할 수 있는 단계입니다.

transform 또는 opacity를 애니메이션하는 것은 전적으로 합성 단계에서 수행될 수 있습니다. 브라우저는 요소를 한 번 칠하고, 자신의 합성 레이어(GPU가 독립적으로 이동하고 혼합할 수 있는 별도로 칠해진 표면)로 승격시킨 후, 매 프레임마다 그 레이어를 새로운 위치 또는 불투명도로만 재합성합니다. 레이아웃 없음, 재칠하기 없음. 대신 widthtop을 애니메이션하는 것은 레이아웃을 무효화하며, 브라우저가 모든 단일 프레임에서 기하학을 재계산하고 재칠하도록 강제하므로, 이것이 로드 아래에서 프레임을 놓는 것입니다.

css
/* 합성 전용: 저렴한, GPU 친화적 */
.card {
  transition: transform 0.2s, opacity 0.2s;
}

/* 매 프레임마다 레이아웃과 칠하기를 트리거합니다: 이것들 애니메이션을 피합니다 */
.card-heavy {
  transition: width 0.2s, top 0.2s;
}

will-change를 사용하면 속성이 곧 애니메이션되려고 하므로 브라우저가 미리 레이어를 승격시킬 수 있다는 힌트를 줄 수 있습니다: will-change: transform. 드물게 사용하고 애니메이션이 끝나면 제거합니다. 각 승격된 레이어는 메모리가 들기 때문이며, 모든 것을 승격시키면 목적이 무효화되고 페이지를 느리게 할 수 있습니다. 그것을 측정된 덜거덕거림을 위한 목표한 수정으로 처리합니다. 기본값이 아닙니다.

이완(easing)은 정확히 명명할 가치가 있는 다른 레버입니다. 타이밍 함수는 경과 시간을 진행에 매핑하는 곡선입니다. ease-out(빠른 후 느려짐)은 입장에 맞습니다. 요소가 도착하고 정착하므로; ease-in은 퇴장에 맞습니다; linear는 스피너 같은 연속 모션에 맞습니다. 무엇이든 사용자 정의에 대해, cubic-bezier(x1, y1, x2, y2)는 두 제어점에 의해 곡선을 정의하며, 내장 키워드들은 이름이 내장 입방 베지어입니다. 성능 예산을 염두에 두세요: 프레임의 시간(60fps에서 약 16ms) 내에 머물기를 목표로, 합성 전용 속성을 애니메이션합니다, 그리고 will-change에 도달하기 전에 프로파일러에 도달합니다. 사용자 정의 속성에서 이들 중 여러 개를 구동하면, 전체 인터페이스에서 지속 시간과 이완을 일관되게 유지할 수 있습니다.

JunoWhat to animate, and why it matterstransformopacity를 애니메이션하고, width, height, top, margin 애니메이션을 피합니다. 이 두 가지가 브라우저가 움직이기에 가장 저렴하므로, 모션이 느린 장치에서도 매끄럽게 유지됩니다. 당신은 나중에 완전한 이유를 배울 것이지만, 습관은 지금부터 시작할 가치가 있습니다.
JunoWhat to animate, and why it matterstransformopacity로 모션을 표현합니다: top 대신 translate를 사용, width 대신 scale을 사용, 0에서 height를 애니메이션하는 대신 opacity를 페이드합니다. 레이아웃 속성을 애니메이션하면 브라우저가 매 프레임마다 레이아웃을 다시 하도록 강제하므로, 느린 장치에서 덜거덕거립니다. 이것이 transform이 이웃을 놔두는 이유입니다: 절대 레이아웃을 건드리지 않기 때문입니다.
JunoWhat to animate, and why it matterstransformopacity는 자신의 GPU 레이어에서 합성 단계에서 애니메이션할 수 있으므로, 레이아웃과 칠하기를 건너뜁니다; widthtop은 매 프레임마다 레이아웃을 무효화하고 로드 아래에서 프레임을 놓습니다. 측정된 덜거덕거림을 위한 목표한 수정으로 will-change를 사용하고 후에 제거합니다. 제스처와 일치하는 이완을 선택합니다. 입장에는 ease-out, 그리고 최적화하기 전에 프로파일합니다.