시간차 트랜지션
트랜지션 훅을 사용하면, 리스트 요소를 출력하는 시점에 시간차를 줄 수 있습니다. 이번 문서는 코드가 너무 길고, 책에서 제외한 내용이라 약간의 추가 설명이 들어 있습니다.
데모
사용하고 있는 주요 기능
트랜지션 205페이지
트랜지션 훅 212페이지
산출 속성(computed) 120페이지
소스 코드
동적으로 리스트 만들기
우선 트랜지션을 적용할 동적 리스트를 생성합시다.
리스트에 요소를 추가&제거하고, current
속성을 사용해서 특정 위치의 요소를 추출하는 내용을 구현했습니다.
<template>
<div class="example">
<p>
<button @click="doAdd">추가</button>
<button @click="current=1">전체</button>
<button @click="current=n" v-for="n in [3,5]" :key="n">
{{n}}의 배수
</button>
</p>
<transition-group tag="ul" class="list">
<li v-for="(item, index) in filteredList"
:data-index="index"
:key="item"
class="item"
@click="doRemove(item)">{{ item }}</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
current: 1,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
computed: {
filteredList() {
return this.list.filter(el => el % this.current === 0)
}
},
methods: {
doAdd() {
const newNumber = Math.max.apply(null, this.list) + 1
const index = Math.floor(Math.random() * (this.list.length + 1))
this.list.splice(index, 0, newNumber)
},
doRemove(item) {
this.list.splice(this.list.indexOf(item), 1)
}
}
}
</script>
<style src="./style.css" scoped></style>
렌더링한 요소의 인덱스를 트랜지션 훅에서 추출할 수 있게 <li>
태그에 data-index
라는 속성을 지정했습니다.
트랜지션 전용 CSS에서는 '추가될 때 왼쪽에서 페이드인', '제거될 때 오른쪽에서 페이드아웃'되는 스타일을 적용하고 있습니다.
/* 박스 스타일 정의 */
.list {
width: 240px;
padding: 0;
}
.item {
display: inline-flex;
justify-content: center;
align-items: center;
margin: 4px;
width: 40px;
height: 40px;
background: #f5f5f5;
}
/* 트랜지션 전용 스타일 */
.v-enter-active, .v-leave-active, .v-move {
transition: opacity 1s, transform 1s;
}
.v-leave-active {
position: absolute;
}
.v-enter {
opacity: 0;
transform: translateY(-20px);
}
.v-leave-to {
opacity: 0;
transform: translateY(20px);
}
훅으로 지연 추가하기
리스트 트랜지션에서는 각 요소에 대해서 다른 트랜지션 훅을 적용할 수 있습니다. 인덱스 숫자를 기반으로 딜레이를 적용하는 함수를 정의해서 훅을 합시다.
<transition-group tag="ul" class="list"
@before-enter="beforeEnter"
@after-enter="afterEnter"
@enter-cancelled="afterEnter">
methods: {
// ...
// 트랜지션을 시작할 때 인덱스 * 100ms만큼의 딜레이를 적용합니다.
beforeEnter(el) {
el.style.transitionDelay = 100 * parseInt(el.dataset.index, 10) + 'ms'
},
// 트랜지션을 완료하거나 취소할 때는 딜레이를 제거합니다.
afterEnter(el) {
el.style.transitionDelay = ''
}
}
추가한 코드는 다음과 같습니다.
<template>
<div class="example">
<p>
<button @click="doAdd">추가</button>
<button @click="current=1">전체</button>
<button @click="current=n" v-for="n in [3,5]" :key="n">
{{n}}의 배수
</button>
</p>
<transition-group tag="ul" class="list"
@before-enter="beforeEnter"
@after-enter="afterEnter"
@enter-cancelled="afterEnter">
<li v-for="(item, index) in filteredList"
:data-index="index"
:key="item"
class="item"
@click="doRemove(item)">{{ item }}</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
current: 1,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
computed: {
filteredList() {
return this.list.filter(el => el % this.current === 0)
}
},
methods: {
doAdd() {
const newNumber = Math.max.apply(null, this.list) + 1
const index = Math.floor(Math.random() * (this.list.length + 1))
this.list.splice(index, 0, newNumber)
},
doRemove(item) {
this.list.splice(this.list.indexOf(item), 1)
},
// 트랜지션 시작 때 <인덱스>*100ms 만큼의 딜레이 부여
beforeEnter(el) {
el.style.transitionDelay = 100 * parseInt(el.dataset.index, 10) + 'ms'
},
// 트랜지션완료 또는 취소 딜레이 제거
afterEnter(el) {
el.style.transitionDelay = ''
}
}
}
</script>
<style src="./style.css" scoped></style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
요소를 추출하는 것이라면 이것으로도 충분합니다. 이 데모를 보면 추가한 요소가 리스트 뒤에 있을 경우, 그만큼 더 딜레이된다는 것을 알 수 있습니다.
요소를 추가한 때의 지연 조정하기
요소를 추가할 때는 딜레이를 적용하지 않도록 기능을 개선해 봅시다.
data(){
return {
// ...
// 추가 상태를 판단하기 위한 플래그
addEnter: false
}
},
methods: {
// ...
doAdd() {
// 추가 플래그 True로 변경하기
this.addEnter = true
const newNumber = Math.max.apply(null, this.list) + 1
const index = Math.floor(Math.random() * (this.list.length + 1))
this.list.splice(index, 0, newNumber)
},
// ...
beforeEnter(el) {
this.$nextTick(() => {
if (!this.addEnter) {
// 추가가 아닌 경우 딜레이 부여
el.style.transitionDelay = 100 * parseInt(el.dataset.index, 10) + 'ms'
} else {
// 추가된 후에는 플래그 제거
this.addEnter = false
}
})
}
}
수정한 코드는 다음과 같습니다.
<template>
<div class="example">
<p>
<button @click="doAdd">추가</button>
<button @click="current=1">전체</button>
<button @click="current=n" v-for="n in [3,5]" :key="n">
{{n}}의 배수
</button>
</p>
<transition-group tag="ul" class="list"
@before-enter="beforeEnter"
@after-enter="afterEnter"
@enter-cancelled="afterEnter">
<li v-for="(item, index) in filteredList"
:data-index="index"
:key="item"
class="item"
@click="doRemove(item)">{{ item }}</li>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
addEnter: false,
current: 1,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
},
computed: {
filteredList() {
return this.list.filter(el => el % this.current === 0)
}
},
methods: {
doAdd() {
// 추가라면 플레그 설정
this.addEnter = true
const newNumber = Math.max.apply(null, this.list) + 1
const index = Math.floor(Math.random() * (this.list.length + 1))
this.list.splice(index, 0, newNumber)
},
doRemove(item) {
this.list.splice(this.list.indexOf(item), 1)
},
// 트랜지션 시작에서 인덱스*100ms 만큼의 딜레이 부여
beforeEnter(el) {
this.$nextTick(() => {
if (!this.addEnter) {
// 추가가 아니라면 딜레이 적용
el.style.transitionDelay = 100 * parseInt(el.dataset.index, 10) + 'ms'
} else {
// 추가라면 플래그 제거만
this.addEnter = false
}
})
},
// 트랜지션완료 또는 취소 딜레이 제거
afterEnter(el) {
el.style.transitionDelay = ''
}
}
}
</script>
<style src="./style.css" scoped></style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
새로 요소를 추가할 때는 딜레이를 적용하지 않게 트랜지션을 변경해 보았습니다.
참고
스크롤 양에 따른 처리 또는 애니메이션 효과는 웹 사이트의 디자인에서 빼놓을 수 없는 부분입니다.
가상 DOM을 통해 구축되는 DOM에 접근하는 경우 nextTick
을 어떻게 사용하는지가 굉장히 중요한 요소가 됩니다.
nextTick
의 사용 방법에 대해서는 3장에서 자세히 설명합니다.