CSS로 애니메이션을? Transition을 써보자!

Kozel·2023년 8월 3일
5
post-thumbnail

웹페이지를 개발할 때면, 특히나 숙련된 프론트엔드 개발자라면 한번 쯤은 고민해 봤을 것이다.

'애니메이션을 어떻게 구현할까? 나도 살아숨쉬는 페이지를 만들고 싶다!'

뭐 이런 생각들?

필자는 hover의 트리거로 크기가 살짝 변한다던지, 모달을 동적으로 등장시킨다던지,, 그정도 수준으로 CSS를 통해 애니메이션을 구현했었다.

사실 애니메이션이라고 하기 애매할 정도.

그래서 그런지, IT 기업의 삐까뻔쩍한 웹사이트를 보며 '에이 이건 영상이네; gif 무조건이다" 이런 생각을 했는데, 우선 틀린 생각은 아니었다.

맞다. 진짜 웹사이트가 살아 숨쉬고 역동적인 그런 효과는 대부분 영상이나 gif다.


뭐야 그럼 CSS를 왜 공부해? 라고 생각할 수 있지만. 물론 CSS로 표현할 수 있는 것에는 한계는 있지만! 그런 CSS를 어느정도 다루냐에 따라 그 한계가 천차 만별이다.

필자도 이번에 Transition과 Animation을 공부하며 깨달아 버렸다.. 내가 지금까지 하던 CSS는 그냥 애들 장난이었구나.. 이런 생각?

거기다 JS까지 합세한다? 기대보다 엄청난 모션을 제작할수도 있다.

이번 포스터에는 Transition에 대해 알아보겠다.


애니메이션은 어떻게 구현할까?

앞서 말했지만 진짜 입이 떡 벌어지는 애니메이션은 보통 gif와 MP4를 사용한다. 규모가 클수록 gif는 품질이 떨어지기 때문에 MP4를 채택하는 경우가 많다.

하지만 이 둘의 차이는? 개발자가 할 수 있는 범주를 벗어난다는 점!

물론 디자인까지 마스터하신 괴물들도 존재하겠지만.. 나같은 인간은 꿈도 못꾸는 경지다.

그래서 적당히 삐까뻔쩍한 애니메이션을 구현하기 위해 코딩으로 어떻게 할지를 고민해 봐야 하는데,,

예시 중 하나로 LOTTIE 에디터도 있다.

코딩으로 한다는 점. 즉 개발자가 애니메이션을 기깔나게 만들 수 있다는 점에서 굉장히 매력적이지만, 이것도 After Effects라는 도구를 사용하여 구현하기 때문에 공부를 해야하고, 결과물이 멋있는 만큼 구현 난이도도 어렵다.


그렇다면 남은건..? CSS..!

그렇다. 우리에겐 아주 친근한, 웹 개발자라면 한번 쯤은 스쳐지나갔을 CSS가 있다. 그리고 든든한 친구 JS도 있다!

결론은 GIF, MP4, LOTTIE 같은 멋진 결과물을 낼 수 있는 수단이 많지만 이는 디자인 분야의 기술을 요구하거나, 특정 도구에 대한 학습을 요구, 혹은 구현 난이도의 어려움으로 나같이 보통의 개발자는 한계가 있다.

하지만 CSS & JS는 어떤가? 어느정도 퀄리티 있는 결과물, 좋은 품질, 비교적 쉬운 구현 난이도, 용량 적음, 브라우저 성능 향상 등의 아주 많은 장점이 있기에 일반인도 공부만 하면 만족스러운 구현을 할 수 있다!

CSS로 애니메이션을 구현할 수 있는 테크닉 중 하나인 Transition을 공부해보자!

Transition

Transition의 뜻이 뭘까. 바로 전이, 이동이다.

즉, 초기 상태에서 종료 상태로의 이동(변화)을 의미한다!

Transition을 사용하기 위해서는?

다음과 같은 조건이 있다.

  1. 적용되는 대상은 초기 상태의 스타일과, 종료 상태의 스타일에 대한 정의가 필요하다. Transition에 대한 정의도 당연히 필수!
  2. 애니메이션의 출력을 위해 트리거가 필요하다. (ex. onClick, hover... 등등) 이는 css의 가상 선택자나 js의 이벤트 등이 있다.

Transition의 5가지 속성

Transition에는 5가지 속성이 있다. 다음 표를 보자.

속성설명기본 설정 값
transition-property애니메이션을 적용할 CSS 속성을 지정한다.all
transition-duration실행 시간을 설정한다. (필수)0s
transition-timing-function가속, 감속을 설정한다.ease
transition-delay지연 시간을 설정한다.0s
transition위 속성을 속기형 선언한다.

ease? 속기형 속성? 생소한 말들이 보이지만 하나씩 살펴 보며 설명할테니 걱정하지 말자!

위 표를 간단히 설명하자면 크게 4가지 속성이 있고, 해당 속성을 속기형으로 간단히 한 줄로 작성할 수 있다.

대부분 속기형 작성법으로 transition을 다뤘을 거라고 생각된다.

기본 설정 값은 말 그대로 기본적으로 설정되어 있는 값이다.
transition-duration같이 필수로 입력해야 하는 속성이 아닌 경우 따로 작성하지 않으면 기본 값이 들어간다.

이제 속성별로 뜯어보자!

1. transition-property

property 속성은 트리거가 발생할 때 전환될, 즉 애니메이션이 적용될 속성을 정한다.

transition-property에는 다음과 같은 5개의 값을 넣을 수 있다.

transition-property: none | all | property | initial | inherit;
  • none : 모든 속성에 적용하지 않는다.
  • all(기본값) : 모든 속성에 적용한다.
  • property : 개별 적으로 속성을 지정할 수 있다. 여러 개일 경우 쉼표(,)로 구분한다.
  • initial : 기본값으로 설정한다.
  • inherit : 부모 요소의 속성값을 상속받는다.

property일 경우. 개별 적으로 지정할 경우 다음과 같이 할 수 있다.

/* 배경만 애니메이션을 적용할 경우 */
transition-property: background-color;

/* 배경과 너비에 애니메이션을 적용할 경우 */
transition-property: background-color, width;  

글로만 봐서는 이해하기 어려우니 각각 어떤 효과가 있는지 눈으로 확인해보자!

각각의 box에 마우스를 올려보자.

우선 transition-duration는 필수 입력 값이기 때문에 1s로 설정해두었다. 뒤에서 다시 설명하겠지만 애니메이션 전환을 1s동안 하겠다는 의미이다.

마우스를 올려보면, 세 box 모두 변화가 있는 것은 확실하다. 하지만 분명히 차이가 있다.

우선 종료 상태로 너비, 텍스트 색, 배경 색을 설정해두었다.

none은 애니메이션이 적용된 속성 없이 딱딱하게 전환되고, all은 종료 상태로 설정해 둔 세가지 속성 모두 애니메이션이 적용되어 부드럽게 전환되었다.

그렇다면 property는? transition-property에 설정해둔 배경 색만이 애니메이션이 적용되었고 나머지는 적용되지 않는 것이 보인다.


그러면 initial, inherit는 뭘까?

initial은 우선 기본 값으로, transition-property의 기본 값은 all이므로 모든 속성의 전환을 지정한다.

inherit은 부모의 transition-property 속성을 상속받아 적용된다.

특별한 것 없이 다른 속성에도 쓰이는대로 쓰인다.

그런 김에 이후에 initial, inherit 설명은 생략하겠다! 절대 귀찮아서가 아니다. 글을 읽는 분들은 이정도는 기본으로 알고있을거라 생각되기 때문이다. 진짜로

2. transition-duration

위에서도 설명했듯이 트리거를 통해 특정 애니메이션이 몇 초 동안 실행할 지 설정하는 속성이다.

기본 값은 0s이므로 입력하지 않으면 transition이 실행되지 않는다. 그래서 필수 입력 값이다.

시간 단위는 초(s) or 1/1000초(ms)가 가능하다.

어려운 내용은 아니지만, 이것도 예시를 보자.

3. transition-timing-function

해당 속성은 애니메이션의 감속, 가속을 설정한다.

개인적으로 transition 속성 중 가장 배우기 싫지 않나? 싶다. 왜냐면 넣을 수 있는 값이 많기 때문..

transition-timing-function: ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps( n, start|end ) | cubic-bezier( n, n, n, n ) | initial | inherit;

정말 많다. initial과 inherit을 제외 하고도 7가지나 된다.
하지만! 많다고 어려운 것은 아니니 하나씩 살펴보자.

  • ease: 기본 값으로 느리게 시작 한 뒤 빠르게 가속되었다가 다시 느려진다.
  • linear: 속도의 변화 없이 등속도로 작동한다.
  • ease-in: 느리게 시작 한 뒤 빨라진다. 가속을 설정한다.
  • ease-out: 빠르게 시작 한 뒤 느려진다. 감속을 의미한다.
  • ease-in-out: ease와 유사하지만, 변화 정도가 ease처럼 급격하지 않다.
  • cubic-bezier(n, n, n, n): 3차 베이저 곡선을 활용한 것으로 정교한 제어 시 사용된다.
  • steps(n, start | end): 시퀀스 이미지를 컨트롤 할 때 사용된다. steps는 전체 프레임 수를 의미한다.

대충 ease-in-out까지는 어찌저찌 이해한다고 해도 그 뒤로는 뭔말이지? 싶을 것이다.

원래 가장 맛있는 건 나중에 먹는 법이니.. 쉬운 것부터 보자.

딱히 설명할 것이 없다. 주어진 시간인 2s동안 각각 속성에 따라 감속, 가속이 어느 지점에 어떻게 적용되는지의 차이다.

위의 설명을 읽어보며 하나씩 마우스를 올려보자.


3.1. cubic-bezier

문제는 그 밑에 것들이다.
우선 cubic-bezier부터 살펴보자!

큐빅 베지어.. 베지어? 베지어란 뭘까?

CSS를 많이 다뤄보신 분들이라면 들어보신 분들도 있을 것이다.
베지어 곡선. 그래픽에서 곡선을 표현하기 위한 곡선 알고리즘이다.

베지어 곡선에는 1차 베지어 곡선부터 n차까지 쭉쭉쭉 있는데, 보편적으로 접하는 차수는 2차와 3차이다.

2차와 3차 베지어 곡선이 궁금하신 분들은 여기를 참고하길 바란다.

며칠 전 svg에서 path로 곡선을 그리는 기법을 정리한 적이 있는데, 해당 포스트에서 쿼드라틱 베지어가 2차 베지어 곡선, 큐빅 베지어가 3차 베지어 곡선이다.


그렇다면 큐빅 베지어는 몇차수일까?
사실 미리 스포를 했다. 바로 위에서 말했듯 큐빅 베지어는 3차 베지어 곡선 알고리즘을 사용한다.

하지만 뭔가 이상하다. 3차 베지어 곡선이라면

해당 그림 처럼 총 4개의 좌표. 각각 2개 씩 8개의 값이 필요할 텐데 큐빅 베지어 속성에 넣는 값들을 보면 그렇지가 않다.

transition-timing-function: cubic-bezier( n, n, n, n );

이유는 단순하다. 바로 컨트롤 포인트만 조절할 수 있기 때문!

그럼 시작 점과 끝 점은 어디일까? 기본으로 우상향 방향으로 직선이 설정되어 있다.

그러니까 기본적으로 다음과 같이 설정되어있다는 말이다.

transition-timing-function: cubic-bezier(0.25, 0.25, 0.75, 0.75);

여기서 오해하면 안되는게 시작 점과 끝점이 저렇게 (0, 0), (1, 1)로 기본적으로 세팅되어 있다는 말이지 transition-timing-function의 가본값이 저 상태라는 것은 아니다.
위에서 말했듯 기본 값은 ease이며 ease의 컨트롤 포인트 위치는 위 그림과 전혀 다르다.

베지어 곡선이 뭔지도, 컨트롤 포인트가 있다는 것도 알았다. 그래서 저 선을 어디다가 쓰느냐? 이게 의문일 것이다.

사실 위 그림을 자세히 살펴보면 답을 알 수 있다.

하단과 좌측에 각각 Time, Animation이 보이는가? 딱 느낌이 온다.

아! 시간에 따라 애니메이션이 얼만큼 변화하는지 나타낸거구나!

여기서 한발 더 나아가서 위 그림은 등속 그래프니까 위 처럼 설정하면 속성 값 'linear'와 똑같이 동작하겠구나! 까지 알 수 있다.

그렇다면 기본적인 속성 값들을 큐빅 베지어 속성으로 어떻게 표현할 수 있는지 알아보자!

  • ease: 기본 값으로 느리게 시작 한 뒤 빠르게 가속되었다가 다시 느려진다.
  • linear: 속도의 변화 없이 등속도로 작동한다.
  • ease-in: 느리게 시작 한 뒤 빨라진다.
  • ease-out: 빠르게 시작 한 뒤 느려진다.
  • ease-in-out: ease와 유사하지만, 변화 정도가 ease처럼 급격하지 않다.

필자는 속성마다 큐빅베지어를 기반으로 그래프를 그려보며 속도의 변화에 대해 더 쉽게 이해할 수 있었다.

특히나 ease-in-out은 ease처럼 변화 정도가 급격하지 않다는 말이 무슨 말인지 와닿지가 않았는데, 그래프를 보니 어떤가, 아! 이 차이구나! 싶었다.

결론은 큐빅 베지어를 통해 애니메이션의 속도 변화를 좀더 세밀하게 할 수 있다는 거고, 난이도는 그에 맞게 어렵다! 라는 점이다.

3.2. steps

steps는 gif랑 비슷하다고 생각한다.

원리를 설명하자면 72프레임의 애니메이션을 만든다고 가정했을 때 72개의 자연스러운 그래픽 이미지를 세로로 쭉 붙이고, 해당 이미지를 위에서 아래로 한칸씩 연속해서 보여주는 것이다.

예를 들어 다음과 같은 이미지가 있다고 가정하자.

너무 길어서 좀 축소해서 왔다;
잘 안보이겠지만 빨간색 박스가 점점 옅어지는 과정을 72번으로 쪼개어 이어 붙인 이미지다.

빨간 박스를 감싸는 흰 박스의 너비와 높이는 각각 100px이다.

해당 이미지를 steps를 통해 애니메이션으로 돌려보자.

<!-- html -->
<div class="steps"></div>
/* css */
.steps {
	float:left;
	width: 100px;
    height: 100px;
	margin: 30px;
	background-image: url(./steps.jpg);
	background-size: 100%;
    
    transition: 4s steps(72);
}

.steps:hover {
    background-position-y: -7200px;
}
  • 결과물

위 코드에서는 72장을 4초동안 연속적이게 설정하였는데, 즉 18fps로 재생한 것이다.

여기서 소소한 팁은 위에서는 종료 상태를 -7200px로 해주었지만 초기 상태에서 높이가 100px로 나타나있으니 만약 -7200%로 변경해준다면 확장성 부분에서 유용하게 쓰일 것이다.

필자가 만든 결과물은 굉장히 심플하고 별거 없어보이지만, 훨씬 복잡하고 역동적인 그래픽 소스가 있다면 steps를 통해 멋진 결과물을 얻을 수 있을 것이다.

4. transition-delay

delay, 말 그대로 지연 시간을 설정할 수 있다.

기본 값은 0s며 시간 단위는 초(s) or 1/1000초(ms)가 가능하다.

직접 보자!

마우스를 올렸을 때 2초 뒤에 애니메이션이 활성화 되는 것을 확인 할 수 있다.

재밌는 점은 다시 복구 될 때도 2초가 지연되어 복구가 된다는 점!

5. transition

transition에서 써먹을 수 있는 속성을 전부 배웠다.

근데 뭔가 찜찜하다..? 다음의 코드를 보자.

/* 개별 트랜지션 선언 시 */
transition-property: height;  
transition-duration: .2s; 
transition-timing-function: ease; 
transition-delay: 10s; 	
+) 0.xx.. 의 수를 적을 때 맨 앞의 0은 생략 가능하다.

아무리 생각해도 이상하다. 이 많은 문자를 직접 입력하라고? 이게 개발..?

그래서 transition 속기형을 배워야 한다!

실제 개발할 때도 많아야 2개의 개별 transition을 설정하는 것이 아니면 대부분 transition 속기형을 활용할 것이다.

우선 간단히 구조를 살펴보자면..

transition: property duration timing-function delay | initial | inherit;

property, duration, timing-function, delay가 순서대로 위치한 모습을 볼 수 있다.

그럼 이 순서를 꼭 지켜야 할까? 그건 아니다. 권장 사항일 뿐이지 편한대로 해도 된다.

단! duration과 delay는 반드시 순서를 지켜야 한다.

이유는 두 속성의 단위가 똑같기 때문에 CSS는 앞에 나온 시간 단위의 값을 duration이라고 인식하기 때문이다.

그렇다면 위 조건에 따라 위에서 개별 선언 했던 예시를 속기형으로 바꿔보자!


/* 트랜지션 속기형(권장순서: property duration timing-fn delay) */
transition: height .2s ease 10s;

어떤가? 훨씬 깔끔하다. 순서만 잘 기억한다면 직관적이고 작성 난이도도 매우 쉬워졌다!

여기서 문제, 다음 개별 transition 선언은 어떻게 해야하나?

transition-property: height, width, color;  
transition-duration: .1s, .2s; 
transition-timing-function: ease, linear, ease; 
transition-delay: .3s, 0s, .4s; 	

머리가 아파온다.. 필자는 참고로 처음에 전혀 틀린 답을 적었다.

2가지만 기억하면 된다.

  1. roperty duration timing-fn delay 순서로 나열한 것을 한 단위로, 쉼표(,)로 구분하여 적는다.
  2. 개별 property 지정 보다 duration, timing-fn, delay 수가 적은 경우는 다시 앞으로 가서 순회한다.

1번은 그럭저럭 이해가 가도, 2번은 말로는 이해하기 쉽지 않다.

좀 더 쉽게 한 단위씩 묶어 보자!

이해가 가는가?

우선 개별 property는 총 3개. 각각에 매칭되는 속성이 timing-function과 delay는 하나씩 매칭이 되지만 duration은 2개 이므로 'color' 속성에 어떤 걸 설정해야 하는지 감이 안잡힐 것이다.

이때는 기본 값을 설정해 주는 것이 아닌 다시 앞으로 돌아가서 '.1s'를 설정해 준다!

이해가 쉽게 다른 예시를 들자면

transition-property: A, B, C, D, E;
transition-delay: a, b, c;

위 예시의 매칭은 다음과 같다.

A-a, B-b, C-c, D-a, E-b

이정도면 이해가 완벽히 됐을거라고 생각된다!


각설하고, 위에서 말했던 문제의 답을 적어보자!

transition: height .1s ease .3s, width .2s linear 0s, color .1s ease .4s;

뭔가 아쉽다.. 당연하다 굳이 적어도 되지 않을 기본 값도 적었기 때문! 기본 값을 생략해 보자.

transition: height .1s .3s, width .2s linear, color .1s .4s;

훨씬..! 까지는 아니고 조금은 줄여졌다.
따지고 보면 개별 transition 선언에 비하면 훨씬 줄여진 것도 맞다!


transition에 대해 배워보았다.

오늘 사용한 예시는 엄청 심플하고 별거 없어보이지만.. 맞다. 당연하다. 쉬운 것만 만들었으니까!

하지만 결코 transition으로 구현할 수 있는 범위가 좁은 것은 아니다. 웹 페이지의 배경을 채우는 휘향찬란한 걸 만들기에는 어려움이 있겠지만 다양한 인터페이스의 모션, 아이콘의 디테일한 움직임을 구현하기에 충분하다.

그리고 CSS에는 Animation 속성도 있다. transition보다 구현 난이도가 어렵지만, 훨씬 디테일한 구현이 가능한!

다음 시간에는 Animation에 대해 학습 후 정리하고 transition과의 차이도 알아보겠다.

profile
front-end developer

2개의 댓글

comment-user-thumbnail
2023년 8월 3일

유용한 정보 감사합니다 짱짱 ^_^

1개의 답글