이글은 An Interactive Guide to Keyframe Animations 를 번역한 글 입니다. 오역이 있을 수 있습니다.
이 글에서, CSS keyframe에 관해 깊게 살펴 볼 것 입니다. 동작하는 방식과, 멋있는 애니메이션 효과를 만드는 방법을 알아 보겠습니다.
CSS frame의 핵심은 서로다른 두 CSS를 연결하는 것 입니다.
아래는 -100%의 위치에서 0%로 요소를 수평으로 이동시키는 keyframe 입니다.
@keyframes slide-in {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
@keyframes
선언문에는 이름이 필요합니다. 위의 경우 slide-in
이라는 이름을 지정했습니다. 이름은 전역변수로 생각하면 됩니다.
Keyframe 애니메이션은 전역적으로 재사용되게끔 설계되었습니다. 이렇게 지정한 애니메이션을 animation
속성에 지정하면 됩니다.
transition
속성과 같이 animation
속성도 지속 시간을 설정해 줘야 합니다. 위 예시에서는 1초 (1000ms) 동안 애니메이션이 지속됩니다.
브라우저는 from
과 to
블록내의 선언을 지속시간동안 수행합니다. 이는 속성이 설정되자마자 일어납니다.
복수의 속성들을 하나의 애니메이션에 선언할 수도 있습니다. 아래는 여러개의 속성을 변경시키는 예시 입니다.
CSS Transition 에서 timing function들에 대해 살펴 봤습니다.
transition
속성과 마찬가지로 같은 timing function들을 적용할 수 있습니다. 또한 transition
과 같이 기본값은 ease
입니다.
animation-timing-function
으로 지정할 수 있습니다.
기본적으로, keyframe 애니메이션은 한번만 실행됩니다. 하지만 animation-iteration-count
속성으로 조절할 수 있습니다.
위 경우 처럼 정수를 직접 지정하기 보다는, infinite
라는 값을 많이 사용합니다.
이 값은 로딩 스피너와 같은 곳에 지정할 수 있습니다.
linear
timing function을 사용합니다.from
, to
키워드 말고, 퍼센트를 사용할 수도 있습니다. 이를 통해 두 단계 이상의 애니메이션 효과를 줄 수 있습니다.
퍼센트는 애니메이션의 진척도를 의미합니다. from
은 0%의 문법적 설탕(syntactic sugar)이며 to
는 100%의 문법적 설탕입니다.
중요한 점은, timing function은 각 단계에 적용된다 라는 사실입니다. 모든 애니메이션에 하나의 ease
효과가 적용되는 것이 아닙니다.
아래의 예시에서, 두개의 스피너는 2초동안 1바퀴를 돕니다. 하지만 multi-step-spin
은 4단계로 구성되어 있으며, 각 단계마다 timing function이 적용되어 있습니다.
불행히도, CSS keyframe 만으로는 위의 동작을 컨트롤 할 수 없습니다. 하지만 Web Animations API를 이용하면 설정할 수 있습니다. 만약 각 단계에서 적용된 ease
효과가 문제가 된다면 이 문서를 참고해 보세요.
수축과 팽창을 하는 '숨쉬는' 효과를 요소에 준다고 가정합시다.
3단계의 애니메이션을 설정할 수 있습니다.
애니메이션 단계의 절반동안은 원래의 크기의 1.5배로 확장 됩니다. 최댓값에 도달하면, 남은 절반동안 원래의 크기로 돌아 갑니다.
위 방법은 동작하지만, 같은 효과를 만들기 위한 더 좋은 방법이 있습니다. animation-direction
속성을 사용하면 됩니다.
animation-direction
은 순서를 조정합니다. normal
이 기본값이며 0%에서 100%까지 지정한 시간만큼 움직입니다.
흥미로운 점은 alternate
값을 지정할 수 있다는 점입니다. 이 값은 normal
과 reverse
사이를 왔다갔다 하며 반복합니다.
팽창과 수축효과를 하나의 애니메이션에 지정하는 것 보다, 팽창효과만 지정하고 다음 반복시 역으로 진행하게 하면 수축효과를 줄 수 있습니다.
이 글에서 많은 애니메이션 속성들을 살펴 보았고 이 값들은 모두 입력하기에 너무 많습니다.
transition
과 같이, 모든 속성들은 축약형으로 쓸 수 있습니다. 위 예시는 아래와 같이 쓸 수 있습니다.
.box {
/*
animation: grow-and-shrink 2000ms;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-direction: alternate;
*/
animation: grow-and-shrink 2000ms ease-in-out infinite alternate;
}
순서는 중요하지 않습니다. 대부분의 경우, 원하는 순서대로 값들을 지정할 수 있습니다. 따라서 순서를 외울필요는 없습니다.
하지만 animation-delay
경우는 예외입니다. 이 값은 지속시간 뒤에 와야합니다. 두 속성 모두 시간을 의미하기 때문입니다.
이 때문에, 이 글을 쓴 필자는 축약형 표현에서 animation-delay
속성을 제외해 따로 선언해 주는 것을 선호합니다.
.box {
animation: grow-and-shrink 2000ms ease-in-out infinite alternate;
animation-delay: 500ms;
}
아마 keyframe 애니메이션 중 가장 헷갈리는 속성일 것입니다.
요소가 사라지는 효과를 주려고 합니다. 아래의 예시에서 애니메이션은 정상동작 하지만, 효과가 끝나면 원래의 상태로 돌아갑니다.
요소의 opacity
를 시간에 따른 그래프로 표현한다면 아래와 같이 표현할 수 있습니다.
왜 요소의 가시성이 원래대로 돌아가는 걸까요? from
과 to
블록 안에 선언된 것들은 애니메이션이 동작할때만 적용됩니다.
1000ms 후에, 애니메이션은 사라지게 됩니다. to
블록안에 선언된 것들은 사라지고, 다른곳에서 선언된 CSS가 적용됩니다. 위 예시에서 다른곳에 opacity
속성을 지정하지 않았기 때문에, 기본값인 1로 돌아갑니다.
이 문제를 해결하기 위한 방법중 하나는 .box
선택자에 opacity
속성을 지정하는 것입니다.
애니메이션이 동작하는 동안, @keyframes
안의 선언문은 .box
선택자 규칙보다 우선순위가 높습니다. 하지만 애니메이션이 끝나게 되면, 애니메이션 안의 선언들은 무효가 되며, .box
선택자 안의 규칙이 적용됩니다.
CSS를 to
블록안의 선언문과 일치하도록 업데이트 할 수 있습니다. 그런데 이 방법이 최선일까요?
상태를 원래대로 되돌리는 선언을 하는 대신에, animation-fill-mode
를 사용해 다른 접근을 해봅시다.
animation-fill-mode
를 이용하면 애니메이션의 최종값을 유지할 수 있습니다.
forwards
이름이 혼란스럽지만, 위 그래프를 보면 이해가 될 것 입니다.
애니메이션이 끝나면, animation-fill-mode: forwards
는 마지막 블록의 선언을 복사해 유지하도록 합니다.
애니메이션 효과가 바로 시작되는 것을 원하지 않을때도 있습니다. transition
과 같이 animation-delay
로 지연시간을 정할 수 있습니다.
처음 0.5초 동안, 요소는 완전히 보입니다.
from
과 to
블록안의 CSS는 애니메이션이 동작하는 동안 적용됩니다. animation-delay
는 포함되지 않습니다. 따라서 0.5초 동안, from
블록안의 CSS는 적용되지 않습니다.
animation-fill-mode
의 또다른 값으로 이 상황을 해결할 수 있습니다. backwards
는 애니메이션의 처음 상태를 유지하게 해줍니다.
만약 처음과 끝 상태 모두 유지하려면 어떻게 해야 할까요? 그때는 both
라는 값을 사용하면 됩니다.
다른 애니메이션 속성과 마찬가지로 축약형으로 쓸 수 있습니다.
.box {
animation: slide-in 1000ms ease-out both;
animation-delay: 500ms;
}
튀는 공 효과를 주기 위해, 지금까지 배운 것들을 모두 사용해 봅시다.
CSS 애니메이션은 전역적으로 재사용되게끔 설계되어 있지만, 이 애니메이션은 항상 20px 만큼 튀어 오르게 되어있습니다. 만약 다른 요소들에게 다른 높이를 지정하면 더 멋있지 않을까요?
CSS 변수를 사용하면, 이 부분을 해결할 수 있습니다.
이 방법을 사용하면 애니메이션을 재사용할 수 있습니다.