텍스트 애니메이션
데모
사용하고 있는 주요 기능
클래스와 스타일 데이터 바인딩 62페이지
리스트 트랜지션 205페이지
산출 속성(computed) 120페이지
워처(watch) 128페이지
소스 코드
<template>
<div class="example">
<h3>TextAnime1 <button @click="anime1=!anime1">변경하기</button></h3>
<TextAnime1 v-if="anime1"/>
<p>SCSS로 애니메이션 지연 주기</p>
<hr>
<h3>TextAnime2 <button @click="anime2=!anime2">변경하기</button></h3>
<TextAnime2 v-if="anime2"/>
<p>바인딩으로 애니메이션 지연 주기</p>
<hr>
<h3>TextAnime3</h3>
<p><label><input type="checkbox" v-model="autoplay"> 5초마다 자동으로 문자 변경하기</label></p>
<TextAnime3 :autoplay="autoplay"/>
<p><code>transition-group</code>의 <code>v-move</code> 를 사용했습니다.</p>
</div>
</template>
<script>
import TextAnime1 from './TextAnime1'
import TextAnime2 from './TextAnime2'
import TextAnime3 from './TextAnime3'
export default {
components: {
TextAnime1,
TextAnime2,
TextAnime3
},
data() {
return {
anime1: true,
anime2: true,
autoplay: true
}
}
}
</script>
TextAnime1
문자 수가 적어서 스타일을 조합해서 애니메이션을 만들고자 하는 경우에는 이를 활용하는 것이 좋습니다.
<template>
<div class="TextAnime1">
<span v-for="(t, index) in text" :key="index" v-text="t" class="item delay-anime"/>
</div>
</template>
<script>
export default {
data() {
return {
text: '고양이도 할 수 있는 Vue.js'
}
}
}
</script>
<style lang="stylus" scoped>
@keyframes text-in {
0% {
transform: translate(0, -20px);
opacity: 0;
}
}
.item {
display: inline-block;
min-width: 0.3em;
font-size: 2rem;
animation: text-in 0.8s cubic-bezier(0.22, 0.15, 0.25, 1.43) 0s backwards;
}
for co in 0 .. 12 {
.delay-anime:nth-child({co + 1}) {
animation-delay: co * 100ms + 200ms;
}
}
</style>
당연히 고정 문자열이라면 v-for
가 아니라 정적 콘텐츠를 바로 넣어서 사용하는 것이 성능면에서 더 낫습니다.
TextAnime2
문자 수가 많고, 변화가 많을 수 있는 경우라면 이를 활용하는 것이 좋습니다.
<template>
<div class="TextAnime1">
<span
v-for="(t, index) in text"
:key="index"
class="item"
:style="{animationDelay: index*100+'ms'}"
v-text="t"
/>
</div>
</template>
<script>
export default {
data() {
return {
text: '고양이도 할 수 있는 Vue.js'
}
}
}
</script>
<style scoped>
@keyframes text-in {
0% {
transform: translate(0, -20px);
opacity: 0;
}
}
.item {
display: inline-block;
min-width: 0.3em;
font-size: 2rem;
animation: text-in .8s cubic-bezier(0.22, 0.15, 0.25, 1.43) 0s backwards;
}
</style>
TextAnime3
키가 같다면 v-move
가 적용되는 것을 사용해서 문자와 인덱스를 조합해서 키를 생성하고 있습니다.
문자 수가 많으면 비용이 약간 높아질 수 있으므로 인스턴스 초기화와 메시지 편집이 일어날 때, 미리 분자열을 분석하고 키를 생성해 두는 점을 주목해 주세요.
<template>
<div class="TextAnime1">
<textarea v-model.lazy="editor" style="width:80%;height:40px;"></textarea>
<transition-group tag="div" class="title">
<span v-for="el in text" :key="el.id" class="item" v-text="el.text"/>
</transition-group>
</div>
</template>
<script>
export default {
props: {
autoplay: Boolean
},
data() {
return {
timer: null,
index: 0,
// 원본 메시지
original: [
'기능별로 Vue.js를 설명하는 입문서입니다. Vue.js를 한 번도 다루어 본 적이 없는 분과 이미 Vue.js를 사용하고 있는 분 모두 즐겁게 책을 볼 수 있을 것입니다.',
'Vue.js는 직관적으로 사용할 수 있는 내용이 많습니다. 그래서 어떻게든 작동이 되니 자세한 이해 없이 코드를 작성하는 경우가 많습니다. 많이들 실수하는 코드를 살펴보며 어떠한 장점과 단점이 있는지 분석하는 내용도 담고 있습니다.',
'Vue.js는 직관적으로 사용할 수 있는 내용이 많습니다. 그래서 어떻게든 작동이 되니 자세한 이해 없이 코드를 작성하는 경우가 많습니다. 많이들 실수하는 코드를 살펴보며 어떠한 장점과 단점이 있는지 분석하는 내용도 담고 있습니다.'
],
// 분해한 메시지
messages: [],
text: ''
}
},
computed: {
editor: {
get() { return this.text.map(e => e.text).join('') },
set(text) { this.text = this.convText(text) }
}
},
watch: {
autoplay(val) {
clearTimeout(this.timer)
if (val) {
this.ticker()
}
}
},
methods: {
// 데모 전용 타이머
ticker() {
this.timer = setTimeout(() => {
if (this.autoplay) {
this.index = this.index < this.messages.length-1 ? this.index + 1 : 0
this.text = this.messages[this.index]
this.ticker()
}
}, 5000)
},
// 텍스트를 분리해서 객체로 리턴하기
convText(text) {
const alms = {}
const result = text.split('').map(el => {
alms[el] = alms[el] ? ++alms[el] : 1
return { id: `${el}_${alms[el]}`, text: el }
})
return Object.freeze(result) // 감시하지 않음
}
},
created() {
this.messages = this.original.map(el => this.convText(el))
this.text = this.messages[0]
this.ticker()
}
}
</script>
<style scoped>
.title {
font-size: 2rem;
}
.item {
display: inline-block;
min-width: 0.3em;
}
/* 트랜지션 전용 스타일 */
.v-enter-active,
.v-leave-active,
.v-move {
transition: all 1s;
}
.v-leave-active {
position: absolute;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(-30px);
}
</style>