본문으로 건너뛰기
개발 머꼬
개발 노트Vue
hohyeon.dev17

목록 애니메이션에서 나머지 항목이 순간이동한 이유

  • #CSS
  • #Engineering Note
  • #Vue

문제 발생

목록에 항목을 추가·삭제할 때 애니메이션을 넣으려고 <Transition>을 썼는데 아무 일도 일어나지 않았습니다. <TransitionGroup>으로 바꾸니 추가·삭제는 되는데, 나머지 항목이 순간이동했습니다.

<TransitionGroup name="list" tag="ul">
  <li v-for="item in items">{{ item.name }}</li>
</TransitionGroup>

콘솔에는 key가 필요하다는 경고가 떠 있었습니다.

원인 분석

<Transition>서로 배타적인 하나의 요소가 나타나고 사라지는 것을 다룹니다. 목록처럼 여러 개가 동시에 들고 나는 경우는 <TransitionGroup>의 일입니다.

둘의 차이를 공식 문서가 정리합니다.

  • <TransitionGroup>래퍼 요소를 렌더하지 않지만, tag prop으로 렌더할 요소를 지정할 수 있습니다.
  • 전환 모드(mode)를 쓸 수 없습니다. 배타적인 두 요소를 번갈아 보여주는 것이 아니기 때문입니다.
  • 안의 요소들은 항상 고유한 key 속성이 필요합니다.
  • CSS 전환 클래스는 그룹/컨테이너가 아니라 개별 요소에 적용됩니다.

"순간이동"은 v-move가 없어서입니다. 항목이 추가·삭제되면 나머지 항목의 위치가 바뀌는데, 그 이동에는 별도 클래스(.list-move)가 필요합니다.

해결 방안

  1. key를 반드시 줍니다. 인덱스가 아니라 데이터 고유 값입니다.
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</TransitionGroup>
  1. 이동 전환은 -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);
}
  1. 사라지는 항목을 레이아웃 흐름에서 빼야 합니다. 이게 빠지면 이동 애니메이션 계산이 어긋납니다. 문서가 명시하는 조건입니다.
.list-leave-active {
  position: absolute;
}
  1. display: inline에서는 동작하지 않습니다. 이동 애니메이션은 FLIP 기법으로 위치를 계산하는데, 인라인 요소는 transform이 적용되지 않습니다. inline-block이나 flex/grid 자식으로 만듭니다.

  2. 순차 지연은 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 });
}
  1. prefers-reduced-motion을 존중합니다. 목록이 통째로 움직이는 애니메이션은 특히 부담이 큽니다.

  2. 애니메이션이 정보를 대체하지 않게 합니다. 항목이 사라졌다는 사실은 애니메이션이 아니라 화면 내용과 라이브 리전으로 전달되어야 합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.