목록 애니메이션에서 나머지 항목이 순간이동한 이유
문제 발생
목록에 항목을 추가·삭제할 때 애니메이션을 넣으려고 <Transition>을 썼는데 아무 일도 일어나지 않았습니다. <TransitionGroup>으로 바꾸니 추가·삭제는 되는데, 나머지 항목이 순간이동했습니다.
<TransitionGroup name="list" tag="ul">
<li v-for="item in items">{{ item.name }}</li>
</TransitionGroup>콘솔에는 key가 필요하다는 경고가 떠 있었습니다.
원인 분석
<Transition>은 서로 배타적인 하나의 요소가 나타나고 사라지는 것을 다룹니다. 목록처럼 여러 개가 동시에 들고 나는 경우는 <TransitionGroup>의 일입니다.
둘의 차이를 공식 문서가 정리합니다.
<TransitionGroup>은 래퍼 요소를 렌더하지 않지만,tagprop으로 렌더할 요소를 지정할 수 있습니다.- 전환 모드(
mode)를 쓸 수 없습니다. 배타적인 두 요소를 번갈아 보여주는 것이 아니기 때문입니다. - 안의 요소들은 항상 고유한
key속성이 필요합니다. - CSS 전환 클래스는 그룹/컨테이너가 아니라 개별 요소에 적용됩니다.
"순간이동"은 v-move가 없어서입니다. 항목이 추가·삭제되면 나머지 항목의 위치가 바뀌는데, 그 이동에는 별도 클래스(.list-move)가 필요합니다.
해결 방안
- key를 반드시 줍니다. 인덱스가 아니라 데이터 고유 값입니다.
<TransitionGroup name="list" tag="ul">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</TransitionGroup>- 이동 전환은
-move클래스에 겁니다.
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}- 사라지는 항목을 레이아웃 흐름에서 빼야 합니다. 이게 빠지면 이동 애니메이션 계산이 어긋납니다. 문서가 명시하는 조건입니다.
.list-leave-active {
position: absolute;
}-
display: inline에서는 동작하지 않습니다. 이동 애니메이션은 FLIP 기법으로 위치를 계산하는데, 인라인 요소는 transform이 적용되지 않습니다.inline-block이나 flex/grid 자식으로 만듭니다. -
순차 지연은 JS 훅으로 만듭니다. 데이터 속성으로 인덱스를 넘기고 훅에서 지연을 줍니다.
<li v-for="(item, index) in items" :key="item.id" :data-index="index">function onEnter(el, done) {
gsap.to(el, { opacity: 1, delay: el.dataset.index * 0.15, onComplete: done });
}-
prefers-reduced-motion을 존중합니다. 목록이 통째로 움직이는 애니메이션은 특히 부담이 큽니다. -
애니메이션이 정보를 대체하지 않게 합니다. 항목이 사라졌다는 사실은 애니메이션이 아니라 화면 내용과 라이브 리전으로 전달되어야 합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.