keyframes animation
키프레임 애니메이션 속성은 여러 스타일을 가진 요소의 상태 변화를 통해 transition 보다 견고하고 디테일한 애니메이션을 구현한다.
@Keyframes 전용 규칙을 통해 요소의 스타일과 흐름 맥락을 정의하고, animation 속성으로 후속 처리한다.
키프레임 애니메이션 사용 시 유의할 점
페이지 처음에 보이는 애니메이션은 순수하게 keyframes animation 방법으로 만들었지만, 각 단이 늘어나고 줄어들고 다음 단이 나타나는 등의 이런 복잡한 줄거리나 분기를 구현하는 것은 권장하지 않는다.
코드의 양이 많아지는 것은 차치하더라도, 애니메이션 설계상의 문제로 인한 나쁜 코드 가독성, 그리고 사후 편집이 매우 어렵다. 예를들면, 각 시퀀스간의 딜레이를 1초씩만 바꾸려고 해도 엄청난 작업이 요구된다.
자바스크립트를 쓰지 않는 이상, 선행 애니메이션이 끝난 시점을 알려주는 타임라인 내장 이벤트가 전무하기 때문이다. setTimeout 함수처럼 작성자가 정의한 시간이 지나면 무조건 후행 애니메이션을 실행한다.
keyframes animation 속성은 transition 속성으로 해결하지 못하는 부분을 보완하는 선에서 제한적으로 사용하는 것이 바람직하겠다.
아래 영상에서 2:32초부터 3:35초 구간이 이 부분의 심각성을 잘 보여주고 있다. 간단한 텍스트 효과를 위한 코드의 양도 여기에서 확인해 보자.
기본 마크업
아래 코드는 animation 속성과 @keyframes 규칙 순으로 정의했다. 이 순서는 바뀌어도 상관없다.
animation 속성의 마지막 값인 myAnimation은 그 아래 키프레임 규칙의 이름이다. 이 값은 animation 속성값의 맨 처음으로 옮겨도 상관없다. 읽기 편한 쪽으로 작성한다.
.div1 {
animation: 3s ease-in 1s 2 normal backwards running myAnimation;
}
@keyframes myAnimation {
0% { opacity: 0; }
100% { opacity: 1; }
}
animation 속성
@keyframes 규칙과 함께 사용하며, 규칙 이름을 가져와 처리한다. 각 요소와의 애니메이션 싱크를 위해 단축(Shorthand) 문법을 사용하는 것이 좋으나 먼저 하위 속성부터 알아보자.
하위 속성과 값
.div1 {
animation-name: myAnimation;
animation-delay: 3s;
animation-timing-function: ease-in;
animation-duration: 1s;
animation-iteration-count: 2;
animation-direction: normal;
animation-fill-mode: backwards;
animation-play-state : running;
}
모바일 기기의 가독성을 위해 하위 속성명의 animation- 부분은 생략해서 작성하였으니 유의한다.
하위 속성 | 값 |
---|---|
name | @keyframes Rule에서 선언한 애니메이션 이름 |
delay | 효과의 시작 지연시간. 1s는 1초 |
timing-function | 키프레임 간의 영향력을 조절하여, 움직임에 긴장감을 줄 수 있는 중간값을 정의한다. 자세한 설명은 transition 기사를 참고. 전용 값으로 steps() 메소드가 있으며 후술한다. |
duration | 효과의 지속 시간을 표기. 1s는 1초 |
iteration-count | 애니메이션 반복 횟수. infinite는 loop와 같다. |
direction | 애니메이션 1회 종료 이후의 후속 움직임 정의
|
fill-mode | 애니메이션 실행 전과 후에 대상에 스타일을 적용하는 방법에 대해 정의한다. 자세한 내용은 후술. |
play-state | running, paused 값으로 애니메이션의 실행과 대기를 정의한다. 기본값은 running. |
animation-fill-mode 추가 설명
기본적으로 애니메이션은 한 사이클이 종료되면 요소가 원래의 위치 및 상태로 돌아간다. 예를들어, 한 글자씩 나타나는 텍스트 애니메이션을 생각해보자. 글자들이 하나씩 나타났다가 애니메이션이 끝나면 이내 하나씩 화면에서 사라진다.
이 결과를 의도했을 수도 있지만, 사라지지 않고 그대로 유지하고자 했을수도 있다. animation-fill-mode는 후행 애니메이션과의 연계에도 영향을 주므로 중요한 속성이라 하겠다.
이 속성을 잘 이해하려면 CSS 셀렉터에서 정의한 스타일과 키프레임 규칙에서 정의한 스타일 두 가지로 나눠서 생각해야 한다. 다음 코드를 보자.
div {
left: 50px; /* selector에서 선언한 위치 */
animation-name: myAnimation;
animation-duration: 2s;
animation-fill-mode: forwards;
}
@keyframes myAnimation {
0% { left: 0px } /* @keyframes Rule에서 선언한 위치 */
100% { left: 100px }
}
보는바와 같이 작성자는 같은 left 속성을 셀렉터와 키프레임 규칙 두 군데에 정의할 수 있다. 이제 animation-fill-mode 속성값이 이들 스타일을 어떻게 처리하는지 간단히 살펴보자.
우선 아래 값 리스트에서 none 값만 본다.
- none : 실행 전(selector 위치) → 시작(@keygrames 시작 위치) → 종료(selector 위치)
- forwards : 실행 전(selector 위치) → 시작(@keygrames 시작 위치) → 종료(@keyframes 종료 위치)
- backwards : 실행 전(keygrames 시작 위치) → 시작(@keygrames 시작 위치) → 종료(selector 위치)
- both : 실행 전(keygrames 시작 위치) → 시작(@keygrames 시작 위치) → 종료(@keyframes 종료 위치)
animation-fill-mode: none은 기본값이다. 실행 전(selector 위치)는 애니메이션 요소가 도큐먼트에 처음 로드된 상태로, 셀렉터에서 정의한 left: 50px 상태다. 즉, 화면에 나타나면 왼쪽으로부터 50px 떨어진 위치에 보여진다.
시작(@keygrames 시작 위치)는 애니메이션이 시작됐음을 의미하며, 키프레임 규칙에서 0% 혹은 from에 정의한 스타일이 씌워진다. 따라서 위 예문에서는 left: 0 속성값에 따라 왼쪽에 딱 달라붙어 시작하게 된다. 이처럼 실행 전 스타일과 실행 후 스타일이 다르면 매우 부자연스러운 애니메이션이 된다.
종료(selector 위치)는 애니메이션이 종료되었음을 의미하며, 키프레임 규칙에서 100% 혹은 to에 정의한 스타일이 씌워진다. 위 예문에서는 셀렉터에 정의한 left: 50px로 초기화 됨을 의미한다.
See the Pen CSS 애니메이션 fill-mode by Hyunhwa Jeong (@dev4design) on CodePen.
이제 forwards, backwards, both 값도 앞서 소개한 리스트를 통해 어떻게 애니메이션 될지 파악되므로 따로 설명하지 않는다.
참고로, 처음 예로 들었던 한 글자씩 나타나는 텍스트 애니메이션은 animation-fill-mode: forward를 정의해서 텍스트가 키프레임 종료 위치를 유지하는 것으로 해결할 수 있다.
Steps 추가 설명
steps() 메소드는 animation-timing-function 속성의 값 중 하나로, 키프레임 사이에 인터벌을 두어 스톱 모션과 같은 효과를 준다. → 여기를 클릭해서 샘플도 보자.
인자값으로 스톱 프레임으로 사용할 수(number)와 주기(term)을 받는다. 일반적으로 term 값은 옵션이므로 생략해도 무방하며, 생략하면 end(jump-end) 값과 동일하다.
다음 예문은 10초에 걸쳐 10 스텝을 표현하기 때문에, 1초에 10px씩 오른쪽으로 이동하는 애니메이션이 된다.
/* steps(n, ) */
.div1 {
position: relative;
animation: myAnimation 10s steps(10) infinite;
}
@keyframes myAnimation {
0% { left: 0 }
100% { left: 100px }
}
다양한 term 인자값이 있는데, 그림을 보고 대강의 움직임을 파악하자. → 여기를 클릭해서 term 인자 부분만 바꿔가면서 테스트도 해본다.
![](https://newstoday.io/wp-content/uploads/2016/08/steps-function.png)
단축 문법 - Shorthand
애니메이션 간 싱크를 맞추기 위해서는 단축 문법이 편리하다. 물론 줄거리나 분기가 많다면 이마저도 힘들다.
![애니메이션 속성 설명](https://newstoday.io/wp-content/uploads/2022/05/keyframe-prop.webp)
/* animation : duration timing-function delay iteration-count direction fill-mode play-state name */
.div1 {
animation: 3s ease-in 1s 2 normal backwards running myAnimation;
}
@keyframes myAnimation {
0% { opacity: 0; }
100% { opacity: 1; }
}
생략이 가능한 하위속성이 있고, 생략된 하위속성은 기본값을 사용한다.
/* @keyframes duration | timing-function | delay | name */
animation: 3s linear 1s slidein;
/* @keyframes duration | name */
animation: 3s slidein;
복수 선언
두 개 이상의 키프레임 룰로 정의 된 애니메이션을 함께 정의할 수 있다. 선행 정의 마지막에 콤마(,)를 사용해서 연결한다.
복수로 선언하였다고 해도 후행 애니메이션은 선행 애니메이션의 동작이 끝나기를 기다려 주지 않는다. 오직 animation-delay 속성에 의존해야 한다.
.div1 {
animation:
myAnimation_01 5s,
myAnimation_02 5s linear 5s;
}
위 예문은 5초에 걸쳐 진행되는 선행 애니메이션을 기다리기 위해 후행 애니메이션 마지막 속성에 5초의 시작 딜레이를 주었다
@keyframes 규칙
keyframes animation 사용에 필수인 키프레임 규칙에 대해 알아보자.
CSS 애니메이션은 시작과 끝 두 지점 사이에 중간값을 추가하여 움직임을 자동으로 처리하는 선형 보간(Linear Interpolation) 방식을 사용한다. 초기 시작과 끝 지점은 0%, 100% 혹은 from, to 값이며 이는 동일하다.
@keyframes myAnimation {
0% { ... }
100% { ... }
}
@keyframes myAnimation2 {
from { ... }
to { ... }
}
처음에 소개하기를, 키프레임은 전용 규칙을 통해 요소의 스타일과 흐름 맥락을 정의한다고 하였다. 처음과 끝 두 지점 사이에 키프레임을 추가해서 더 많은 스타일 분기가 일어나면 애니메이션의 흐름은 더욱 다이나믹해 진다.
animation 속성에서 이미 보아왔듯이, 키프레임 규칙에 이름을 정하고, animation 속성에서 그 이름을 매칭하면 하나의 애니메이션이 완성된다.
.div1 {
animation: 3s ease-in 1s 2 normal backwards running myAnimation;
}
@keyframes myAnimation {
0% { opacity: 0; }
100% { opacity: 1; }
}
키프레임 추가
키 프레임을 추가할 때는 퍼센테이지로 구간을 정의한다.
@keyframes myAnimation {
0% { font-size: 10px; }
25% { font-size: 20px; }
50% { font-size: 30px; }
70% { font-size: 20px; }
100% { font-size: 10px; }
}
애니메이션의 시작과 끝이 같다면 콤마(,)로 분리해서 선언해주고, 중간 프레임만 따로 선언해 줄 수도 있다.
@keyframes myAnimation {
0%, 100% { font-size: 10px }
50% { font-size: 50px }
}
마치며
마지막으로, 기사 최상단 커버 애니메이션의 코드는 → 여기에서 확인할 수 있다.