انیمیشن ها (Animations)
تو Vue، «انیمیشن (Animation)» یعنی حرکت یا تغییر نرم. «ترنزیشن (Transition)» یعنی تغییر مرحله ای بین دو حالت. با CSS ساده هم می شود، اما کامپوننت <Transition> اضافه و حذف را راحت تر می کند.
مرور سریع CSS Transition و Animation
می توانیم با v-bind کلاس بدهیم و فقط با CSS حرکت بسازیم. شبیه وقتی دکمه ای را می زنی و با نرم بودن رنگ عوض می شود.
نمونه: چرخش با Transition
<template>
<h1>Basic CSS Transition</h1>
<button @click="this.doesRotate = true">Rotate</button>
<div :class="{ rotate: doesRotate }"></div>
</template>
<script>
export default {
data() {
return {
doesRotate: false
};
}
};
<\/script>
<style scoped>
.rotate {
rotate: 160deg;
transition: rotate 1s;
}
div {
border: solid black 2px;
background-color: lightcoral;
width: 60px;
height: 60px;
}
h1, button, div {
margin: 10px;
}
<\/style>
نمونه: جابه جایی با Animation
<template>
<h1>Basic CSS Animation</h1>
<button @click="this.doesMove = true">Start</button>
<div :class="{ move: doesMove }"></div>
</template>
<script>
export default {
data() {
return {
doesMove: false
};
}
};
<\/script>
<style scoped>
.move {
animation: move .5s alternate 4 ease-in-out;
}
@keyframes move {
from {
translate: 0 0;
}
to {
translate: 70px 0;
}
}
div {
border: solid black 2px;
background-color: lightcoral;
border-radius: 50%;
width: 60px;
height: 60px;
}
h1, button, div {
margin: 10px;
}
<\/style>
کامپوننت <Transition> برای افزودن و حذف
وقتی عنصر با v-if یا v-show ظاهر یا غیب می شود، <Transition> کار را ساده می کند. مثل نشان دادن پیام و محوشدنش.
نمونه: محوشدن هنگام حذف
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
};
},
computed: {
btnText() {
if (this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
};
<\/script>
<style>
.v-leave-from {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
transition: opacity 0.5s;
}
<\/style>
شش کلاس آماده Transition
سه کلاس برای ورود است: v-enter-from، v-enter-active، v-enter-to. سه کلاس برای خروج است: v-leave-from، v-leave-active، v-leave-to. با این ها می توانی هم ورود هم خروج را بسازی.
نمونه: ورود و خروج با حرکت
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
};
},
computed: {
btnText() {
if (this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
};
<\/script>
<style>
.v-enter-from {
opacity: 0;
translate: -100px 0;
}
.v-enter-to {
opacity: 1;
translate: 0 0;
}
.v-leave-from {
opacity: 1;
translate: 0 0;
}
.v-leave-to {
opacity: 0;
translate: 100px 0;
}
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
transition: all 0.5s;
}
<\/style>
کلاس های active و انیمیشن سفارشی
کلاس های v-enter-active و v-leave-active هنگام فرایند ورود یا خروج فعال اند. می توانی keyframes بدهی تا ورود و خروج خاص تر شود.
نمونه: keyframes برای ورود و خروج
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
};
},
computed: {
btnText() {
if (this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
};
<\/script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
p {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
<\/style>
prop نام برای چند Transition
با prop name می توانی کلاس ها را از v- به پیشوند دلخواه تغییر دهی. مثلا swirl-enter-active. این برای چند انیمیشن متفاوت مفید است.
نمونه: دو انیمیشن با نام های مختلف
<template>
<h1>Add/Remove <p> Tag</h1>
<p>Transition دوم name="swirl" دارد تا کلاس ها جدا شوند.</p>
<hr>
<button @click="this.p1Exists = !this.p1Exists">{{btn1Text}}</button><br>
<Transition>
<p v-if="p1Exists" id="p1">Hello World!</p>
</Transition>
<hr>
<button @click="this.p2Exists = !this.p2Exists">{{btn2Text}}</button><br>
<Transition name="swirl">
<p v-if="p2Exists" id="p2">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
p1Exists: false,
p2Exists: false
};
},
computed: {
btn1Text() {
if (this.p1Exists) {
return 'Remove';
}
else {
return 'Add';
}
},
btn2Text() {
if (this.p2Exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
};
<\/script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
.swirl-enter-active {
animation: swirlAdded 1s;
}
.swirl-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, #p2 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p2 {
background-color: lightcoral;
}
<\/style>
هوک های جاوااسکریپتی Transition
هر کلاس رویدادی دارد؛ مثلا بعد از ورود، رویداد after-enter اجرا می شود. می توانی در آن کد بزنی؛ مثل روشن کردن یک باکس.
نمونه: استفاده از after-enter
<template>
<h1>JavaScript Transition Hooks</h1>
<p>پس از اتمام ورود، یک div قرمز نمایش می دهیم.</p>
<button @click="pVisible = true">Create p-tag!</button><br>
<Transition @after-enter="onAfterEnter">
<p v-show="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-show="divVisible">This appears after enter-active.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
};
},
methods: {
onAfterEnter() {
this.divVisible = true;
}
}
};
<\/script>
<style scoped>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
<\/style>
prop appear برای انیمیشنِ بار اول
اگر می خواهی هنگام لود صفحه انیمیشن اجرا شود، prop appear را به Transition بده.
نمونه: اجرای انیمیشن در لود اولیه
<template>
<h1>The 'appear' Prop</h1>
<p>با appear، انیمیشن در رندر اولیه اجرا می شود.</p>
<Transition appear>
<p id="p1">Hello World!</p>
</Transition>
</template>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
background-color: lightgreen;
}
<\/style>
جابجایی بین چند عنصر
می توانی بین چند تصویر سوییچ کنی. شرطش این است که همزمان فقط یکی نمایش شود. از v-if و v-else-if استفاده کن.
نمونه: سوییچ بین تصاویر
<template>
<h1>Transition Between Elements</h1>
<p>با کلیک، تصویر جدید می آید.</p>
<button @click="newImg">Next image</button><br>
<Transition>
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgActive: 'pizza',
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
};
},
methods: {
newImg() {
this.indexNbr++;
if (this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
this.imgActive = this.imgs[this.indexNbr];
}
}
};
<\/script>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
.v-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
<\/style>
mode="out-in"؛ اول خروج، بعد ورود
اگر می خواهی عنصر جدید بعد از خروج قبلی بیاید، از mode="out-in" استفاده کن. این باعث نظم و بدون همپوشانی می شود.
نمونه: out-in برای تصاویر
<template>
<h1>mode="out-in"</h1>
<p>با out-in، ورود پس از خروج انجام می شود.</p>
<button @click="indexNbr++">Next image</button><br>
<Transition mode="out-in">
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
};
},
computed: {
imgActive() {
if (this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
return this.imgs[this.indexNbr];
}
}
};
<\/script>
<style>
.v-enter-active {
animation: swirlAdded 0.7s;
}
.v-leave-active {
animation: swirlAdded 0.7s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
<\/style>
Transition برای کامپوننت های پویا
وقتی بین چند کامپوننت سوییچ می کنی، <component :is> را داخل <Transition> بگذار. سپس ورود و خروجشان انیمیشن می گیرد.
نمونه: سوییچ بین دو کامپوننت
<template>
<h1>Transition with Dynamic Components</h1>
<p>Transition دور کامپوننت پویا قرار می گیرد.</p>
<button @click="toggleValue = !toggleValue">Switch component</button>
<Transition mode="out-in">
<component :is="activeComp"></component>
</Transition>
</template>
<script>
export default {
data() {
return {
toggleValue: true
};
},
computed: {
activeComp() {
if (this.toggleValue) {
return 'comp-one';
}
else {
return 'comp-two';
}
}
}
};
<\/script>
<style>
.v-enter-active {
animation: slideIn 0.5s;
}
@keyframes slideIn {
from {
translate: -200px 0;
opacity: 0;
}
to {
translate: 0 0;
opacity: 1;
}
}
.v-leave-active {
animation: slideOut 0.5s;
}
@keyframes slideOut {
from {
translate: 0 0;
opacity: 1;
}
to {
translate: 200px 0;
opacity: 0;
}
}
<\/style>
نکته: در هر <Transition> فقط یک ریشه قابل انیمیشن مجاز است.
جمع بندی سریع
- برای ورود و خروج، <Transition> بهترین گزینه است.
- از شش کلاس v-* برای فازها استفاده کن.
- با name چند انیمیشن جدا بساز.
- برای لود اولیه از appear کمک بگیر.
- نیاز به نظم داری؟ mode="out-in" بگذار.
بیشتر بخوان: انیمیشن های Vue، کامپوننت ها، و ورودی های فرم.