첫 번째 프로젝트로 이솝 홈페이지를 클론코딩 중인데, 맡은 컴포넌트 중에서 젤 애먹었던 modal menu.... 열고 닫는 건 조건부 렌더링으로 하면 되는 거 알겠는데! 나타날 때랑 사라질 때(특!히! 사라질 때! 😫) 애니메이션 구현하는 방법을 몰라서 🐕고생했다. 💢💢💢
이것저것 막 시도하다 어떻게 성공은 했는데, 까먹을 것 같아서 & 혹시 나 같은 고민에 빠질 사람들을 위해서 해두는 정리! (되긴 되지만.. 맞는 방법인진 모름>.<;;)
먼저 CSS에서 @keyframes로 원하는 효과를 만든다!
@keyframes 애니메이션 이름 { from {원하는 시작 속성} to {원하는 마지막 속성} }
더 복잡한 효과를 원한다면... 원하는 만큼 %마다 끊어서 줄 수도 있음!
@keyframes 애니메이션 이름 { 0% {원하는 속성} 25% {원하는 속성} 50% {원하는 속성} 100% {원하는 속성} }
나는 제품 리스트가 살짝 위에서 아래로 내려오며+투명했다가 나타나게 렌더링되도록 smoothAppear이라는 애니메이션과,
@keyframes smoothAppear {
from {
opacity: 0;
transform: translateY(-5%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
메뉴가 스르륵 열리고 닫히도록 하기 위해서 slider라는 애니메이션을 만들었다.
@keyframes slide {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0%);
}
}
그 다음! 애니메이션을 동적으로 주기 위해 class 명을 하나 만든다. 만약 mount될 때만 애니메이션을 적용시킬 거면 이 단계는 생략해도 됨!
아래처럼 클래스이름을 선택자로 해서 위에서 만든 keyfames로 animation 속성을 넣어 준다.
.클래스명 { animation: [애니메이션 이름] [지속시간] }
지속시간 뒤에 다른 조건을 더 추가할 수도 있다. 자세한 건.... 요기서 CSS animation 참고~~
smoothAppear은 처음 등장할 때만 줄 애니메이션이라서 생략하고, 메뉴 열고 닫기는 아래처럼 만들었다. reverse는 slide keyframes를 반대로 (to 속성부터 from 속성으로) 적용한다는 의미고, &는 네스팅 때문에 써줌!
&.openAnimation {
animation: slide 1s ease-in-out 0s 1 normal forwards;
}
&.closeAnimation {
animation: slide 0.5s ease-in-out 0s 1 reverse forwards;
}
mount될 때만! 애니메이션 주는 건 완전 간단. 그냥 해당 요소의 CSS에 animation 속성만 추가해 주면 됨~~ 그럼 처음 마운트될 때 자동으로 적용된다.
.productCard {
min-width: 340px;
height: 530px;
.
.
.
animation: smoothAppear 1s; <-추가
}
결과물 🌸
반대로 unmount될 때 적용하고 싶으면 위에서 따로 만든 클래스명을 React로 unmount 직전에 넣어 주면 된다. 그러니까
- state 변수를 하나 만들어서 빈 스트링으로 두고,
- 애니메이션을 적용할 요소의 className에 해당 state값을 넣어 둔다. (기존 클래스명이 있다면 백틱 등을 이용!)
- unmount를 실행하기 전에 setState로 빈 스트링을 애니메이션 속성을 담고 있는 클래스명으로 바꿔준다.
여기서 중요한 건❗ unmount 되기 전에❗ 해야 한다. 아니면 애니메이션이 실행될 시간도 없이 바로 컴포넌트가 사라지기 때문.......😓 그래서 setTimeout 같을 써서 unmount 작업을 animation 실행 시간만큼 늦춰 줬다.
<div className={`menuColumn ${animation}`}>
...
</div>
close = () => {
this.setState({
animation: 'closeAnimation',
});
setTimeout(this.props.menuToggle, 500); //menuToggle이 modal과 관련된 상태값을 false로 만들어 컴포넌트를 끄는 함수임!
};
mount될 때와 unmount될 때 둘 다 애니메이션을 주기 위해서는 하나의 과정이 더 필요하다. 단순하게 생각하면 위에서 한 거 둘 다 하면 안 되나?! 싶지만... 안댐..... 그럼 뒤에 추가된 unmount용 애니메이션이 작동을 안 한다! 대체 뭐가 잘못된 건지 몰라서 한참 헤맴 😭
각각 하나씩 할 땐 분명 되는데! 왜 말을 안 듣는지 개발자도구를 켜서 이것저것 확인해 본 결과.... animation 속성이 없는 요소에 animation 속성을 추가하면 추가되는 그 시점에서 효과가 실행되지만, 이미 animation 속성을 가진 요소에 새로 animation을 추가하면 속성이 덮어씌워지기는 하지만 그때 새 애니메이션이 실행되지는 않는 것 같다.
그래서 여러 가지 시도 끝에 찾아낸 방법... mount될 때 animation을 줬다가 ➡ 효과가 끝난 후 animation 속성을 아예 없앴다가 ➡ 다시 unmount 전에 추가하면 된다! 그래서 동적으로 스타일을 바꿔줘야 해서 클래스명이 두 개 필요한 것...!
- 클래스명을 바꾸기 위해 state 변수를 설정하되, 초기값으로 빈 스트링 대신 mount시 animation 속성을 담은 클래스명! 을 준다.
this.state = { animation: 'openAnimation' }
그리고 효과를 적용할 요소의 className에 변수를 넣어 놓는다.
<div className={`menuColumn ${animation}`}> ... </div>
- 마운트 이후 효과가 실행되고 나면 animation 속성을 지워 주기 위해서, componentDidMount 영역에서 state변수를 빈 스트링으로 초기화한다! 대신 이때도 setTimeout으로 this.setState를 애니메이션 실행 시간만큼 지연시켜줘야 함~~~
componentDidMount() { setTimeout(() => { this.setState({ animation: '' }); }, 1000); }
- 그리고 위에서 했던 거랑 똑같이 setTimeout을 이용해 unmount되기 전에 state 변수를 unmount 애니메이션을 담은 클래스명으로 바꿔 준다.
close = () => { this.setState({ animation: 'closeAnimation', }); setTimeout(this.props.menuToggle, 500); };
결과물2 🌸🌸🌸
이런 방법으로 스르륵 등장하고 스르륵 사라지는 컴포넌트를 만들 수 있었다. 분명 더 좋은 방법이나.. 간편한 라이브러리가 있을 것 같지만..... 1차 프로젝트 때 라이브러리 사용은 지양한다고 했기 때문에! 앞으로 차근차근 배워나갈 것...⭐
출처는 길고 긴 내 시행착오~~~ 🤦♀️🤦♀️
ㅆㅅㅌㅊ!