오랜만에 돌아온 그리니 시리즈.. 내 의지를 탓하며 시작합니다.
저번 포스트에서는 svg로 그리니의 몸을 구성하는 요소를 그리고 합쳐보았습니다. 이제는 그냥 가만히 두어도 들숨날숨을 쉬며 살아있는 그리니를 구현해야했는데, 중간에 공부해본 내용 중 animation의 keyframes와 transform을 통해 이를 해결할 수 있었습니다.
본 내용이 이해가 어렵다면 위 링크의 포스트를 먼저 읽고 오시는 걸 추천드립니다.
구현해야하는 animation은 다음과 같습니다.
저번 포스트를 보면 되겠지만 귀찮을 수도 있으니 지금 그리니의 구조를 다시 봐봅시다.
<div id="greeny">
<!-- 오른쪽 날개 -->
<div id="right_wing_box">
<div id="right_wing"></div>
</div>
<!-- 동공 -->
<div id="group_pupil">
<div id="left_pupil"></div>
<div id="right_pupil"></div>
</div>
<!-- 감은 눈 -->
<div id="group_close_eye">
<div id="left_close_eye"></div>
<div id="right_close_eye"></div>
</div>
<!-- 눈 흰자 -->
<div id="group_eye">
<div id="left_eye"></div>
<div id="right_eye"></div>
</div>
<!-- 버튼 -->
<div id="topButton"></div>
<!-- 밴드 -->
<div id="band"></div>
<!-- 모자 -->
<div id="hat"></div>
<!-- 왼쪽 날개 -->
<div id="left_wing_box">
<div id="left_wing"></div>
</div>
<!-- 오른쪽 다리, 신발 -->
<div id="rightLeg"></div>
<div id="right_shoes"></div>
<!-- 왼쪽 다리, 신발 -->
<div id="leftLeg"></div>
<div id="left_shoes"></div>
</div>
코드를 보면 같은 모양인데 좌우가 나눠진 요소는 group 태그로 감싸져있습니다.
노린 건 아니지만 animation을 적용하는 것에 있어서 도움이 됐습니다. 왜냐면 하나의 태그에는 하나의 animation만 적용할 수 있기 때문!
예를 들어 눈동자 요소에는 눈을 좌우로 움직이는 animation과 깜빡이는 animation이 적용되어야 합니다.
하지만 left_pupil
에 두가지 animation을 적용할 수 없기 때문에 left_pupil
, right_pupil
에는 좌우로 움직이는 animation. group_pupil
에는 깜빡이는 animation을 적용했습니다.
이제는 각각 어떻게 구현했는지 살펴보죠.
사람은 가만히 서있을 수 있지만, 캐릭터의 경우 정적으로 있다면 살아있다는 느낌이 들기 부족합니다.
이에 마치 숨을 쉬듯이 살짝의 들썩임이 필요했죠.
@keyframes rightSitDown {
80% {
top: 517px;
transform: scale(1);
}
90% {
top: 533px;
transform: scale(1, 0.6);
}
100% {
top: 517px;
transform: scale(1);
}
}
@keyframes leftSitDown {
80% {
top: 555px;
transform: scale(1);
}
90% {
top: 559px;
transform: scale(1, 0.8);
}
100% {
top: 555px;
transform: scale(1);
}
}
@keyframes moveDown {
0% {
transform: translate(0, 0);
}
20% {
transform: translate(0, 3px);
}
40% {
transform: translate(0, 0);
}
60% {
transform: translate(0, 3px);
}
80% {
transform: translate(0, 0);
}
90% {
transform: translate(0, 10px);
}
100% {
transform: translate(0, 0);
}
}
#rightLeg {
top: 517px;
animation: rightSitDown 5s infinite;
}
#leftLeg {
top: 555px;
animation: leftSitDown 5s infinite;
}
#right_wing_box, #group_eye, #topButton, #band, #hat, #left_wing_box {
animation: moveDown 5s infinite;
}
좌우 다리 요소는 모양이 다르기 때문에 얼마큼 구부릴지 각각 keyframes를 적용해주고, 들썩임을 위해 나머지 요소 전체에 위아래로 흔들리는 keyframes를 적용해줬습니다.
결과는 다음과 같습니다.
뭔가 이상하지 않나요?
네, 모든 요소가 들썩이지만 눈동자 만큼을 고정되어있습니다. 그 이유는 아까 말했듯 하나의 요소에는 하나의 animation만 적용됩니다. 이에 눈동자는 깜빡임도 적용해줘야 했기 때문에 깜빡임을 구현하는 곳에 들써임도 함께 구현했습니다. 이 부분은 후에 나올 겁니다.
한 곳만 응시한다면 역시나 어색할 것입니다. 그렇기에 그리니가 좌우 눈치를 살필 수 있게 해봅시다.
@keyframes pupilSideMove {
70% {
transform: translate(0, 0);
}
85% {
transform: translate(15px, 0);
}
90% {
transform: translate(15px, 0);
}
100% {
transform: translate(0, 0);
}
}
@keyframes eyeSideMove {
70% {
transform: translate(0, 0);
}
85% {
transform: translate(10px, 0);
}
90% {
transform: translate(10px, 0);
}
100% {
transform: translate(0, 0);
}
}
#left_pupil, #right_pupil, #left_close_eye, #right_close_eye {
animation: pupilSideMove 8s infinite;
}
#left_eye, #right_eye {
animation: eyeSideMove 8s infinite;
}
눈 흰자와 눈동자는 역시 다른 정도로 움직여야 하기 때문에 다른 keyframes를 적용해주었습니다.
역시나 눈동자는 위아래로 움직이지 않아 어색하지만 한번만 참아보세요. 바로 다음에 해결할 수 있습니다!
눈을 깜빡이는 동시에 위아래로 움직이는 animation을 적용해봅시다.
눈을 깜빡이는 원리는 다음과 같습니다.
감은 상태의 눈을 구현하고, 해당 눈을 기존의 눈동자와 같은 위치에 opacity
를 0으로 해둡니다. 그리고 적당한 주기로 눈동자와 감은 눈의 opacity
를 상반되게 적용하면 되겠죠.
@keyframes eyeOpen {
0% {
transform: translate(0, 0);
}
20% {
transform: translate(0, 3px);
}
40% {
transform: translate(0, 0);
}
50% { opacity: 0; }
51% { opacity: 1; }
53% { opacity: 1; }
54% { opacity: 0; }
60% {
transform: translate(0, 3px);
}
80% {
transform: translate(0, 0);
}
90% {
transform: translate(0, 10px);
}
100% {
transform: translate(0, 0);
}
}
@keyframes eyeClose {
0% {
transform: translate(0, 0);
}
20% {
transform: translate(0, 3px);
}
40% {
transform: translate(0, 0);
}
50% { opacity: 1; }
51% { opacity: 0; }
53% { opacity: 0; }
54% { opacity: 1; }
60% {
transform: translate(0, 3px);
}
80% {
transform: translate(0, 0);
}
90% {
transform: translate(0, 10px);
}
100% {
transform: translate(0, 0);
}
}
#group_pupil {
animation: eyeClose 5s infinite;
}
#group_close_eye {
animation: eyeOpen 5s infinite;
}
꽤나 길죠? 위아래로 움직이는 animation과 함께 적용하려니 그렇게 됐습니다.
그럼 결과를 봐봅시다.
이제는 위아래 움직임도 어색하지 않고, 정말 눈을 깜빡이는 듯한 모습을 볼 수 있습니다.
마지막이네요. 사람도 가끔 팔짱을 끼거나 팔을 가만히 두지 않죠? 그리니도 그럴겁니다.
날개가 간지럽고 피가 안통할 수 있으니,, 가끔 스트레칭도 해봅시다.
@keyframes flapMove {
96% {
transform: rotate(0);
}
97% {
transform: rotate(10deg);
}
98% {
transform: rotate(0);
}
99% {
transform: rotate(10deg);
}
100% {
transform: rotate(0);
}
}
#right_wing, #left_wing {
transform-origin: 50px 330px;
animation: flapMove 10s infinite;
}
날개는 모양이 같기 때문에 어렵지가 않네요! 하나의 keyframes로 구현해 보았습니다.
마치 비둘기가 날개를 터는 것 같네요.. 큰 차이는 그리니는 귀엽다는 점 입니다.
그렇게 살아 숨쉬는 그리니가 완성됐습니다.
본 글의 gif 모두 버벅임이 느껴지지만 실제 로컬에서 보여지는 그리니는 매우 부드럽게 살아숨쉬듯 보입니다. 뿌듯하네요.
뭔가.. 코드와 gif 만으로 날로먹어버린 포스트라고 느껴지네요.
하지만 뭐 구현에 적용된 개념을 설명하기엔 복잡해지고,, 이미 해당 개념을 정리해둔 포스트가 있어서 그렇게 느껴지나 봅니다.
앞으로.. 크게 두가지 단계가 남았다고 생각됩니다.
둘다 상호작용에 대한 animation을 구현해야 하기 때문에 쉬워보이진 않지만.. 결국 또 언젠간 돌아오겠죠!
눈 껌뻑이는 것부터 날개 파닥파닥까지 너무 귀엽네요 👼 잘 봤습니다!