Hoạt hình Vue
Thành phần <Transition>
tích hợp trong Vue giúp chúng ta tạo hoạt ảnh khi các thành phần được thêm hoặc xóa bằng v-if
, v-show
hoặc với các thành phần động.
Không có gì sai khi sử dụng các hiệu ứng chuyển tiếp và hoạt ảnh CSS đơn giản trong các trường hợp khác.
Giới thiệu ngắn gọn về chuyển tiếp và hoạt ảnh CSS
Phần hướng dẫn này yêu cầu kiến thức về hoạt ảnh và chuyển tiếp CSS cơ bản.
Nhưng trước khi chúng ta sử dụng thành phần <Transition>
tích hợp sẵn dành riêng cho Vue để tạo hoạt ảnh, hãy xem hai ví dụ về cách có thể sử dụng hoạt ảnh và chuyển tiếp CSS đơn giản với Vue.
Ví dụ
App.vue
:
<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>
Chạy ví dụ » Trong ví dụ trên, chúng tôi sử dụng v-bind
để cung cấp cho thẻ <div>
một lớp để nó xoay. Lý do quá trình quay mất 1 giây là do nó được xác định bằng thuộc tính CSS transition
.
Trong ví dụ bên dưới, chúng ta thấy cách chúng ta có thể di chuyển một đối tượng bằng thuộc tính animation
CSS.
Ví dụ
App.vue
:
<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>
Chạy ví dụ »Thành phần <Transition>
Không có gì sai khi sử dụng các hiệu ứng chuyển tiếp và hoạt ảnh CSS đơn giản như chúng tôi đã làm trong hai ví dụ trên.
Nhưng may mắn thay, Vue cung cấp cho chúng ta thành phần <Transition>
tích hợp trong trường hợp chúng ta muốn tạo hoạt ảnh cho một phần tử khi nó bị xóa hoặc được thêm vào ứng dụng của chúng ta bằng v-if
hoặc v-show
, bởi vì điều đó sẽ khó thực hiện được. làm với hoạt ảnh CSS đơn giản.
Trước tiên, hãy tạo một ứng dụng trong đó nút thêm hoặc xóa thẻ <p>
:
Ví dụ
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<p v-if="exists">Hello World!</p>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
}
</style>
Chạy ví dụ » Bây giờ, hãy bọc thành phần <Transition>
xung quanh thẻ <p>
và xem cách chúng ta có thể tạo hoạt ảnh cho việc xóa thẻ <p>
.
Khi sử dụng thành phần <Transition>
, chúng tôi tự động nhận được sáu lớp CSS khác nhau mà chúng tôi có thể sử dụng để tạo hoạt ảnh khi các phần tử được thêm hoặc xóa.
Trong ví dụ bên dưới, chúng tôi sẽ sử dụng các lớp v-leave-from
và v-leave-to
có sẵn tự động để tạo hoạt ảnh mờ dần khi thẻ <p>
bị xóa:
Ví dụ
App.vue
:
<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>
Chạy ví dụ »Sáu lớp <Chuyển tiếp>
Có sáu lớp tự động có sẵn khi chúng ta sử dụng thành phần <Transition>
.
Khi một phần tử bên trong thành phần <Transition>
được thêm vào , chúng ta có thể sử dụng ba lớp đầu tiên này để tạo hoạt ảnh cho quá trình chuyển đổi đó:
- v-nhập-từ
- v-enter-hoạt động
- v-enter-to
Và khi một phần tử bị loại bỏ bên trong thành phần <Transition>
, chúng ta có thể sử dụng ba lớp tiếp theo:
- v-rời-từ
- v-rời-hoạt động
- v-để lại
Lưu ý: Chỉ có thể có một phần tử ở cấp độ gốc của thành phần <Transition>
.
Bây giờ, hãy sử dụng bốn trong số các lớp này để chúng ta có thể tạo hoạt ảnh khi thẻ <p>
được thêm và khi thẻ bị xóa.
Ví dụ
App.vue
:
<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>
Chạy ví dụ » Chúng ta cũng có thể sử dụng v-enter-active
và v-leave-active
để đặt kiểu hoặc hoạt ảnh trong khi thêm hoặc xóa một phần tử:
Ví dụ
App.vue
:
<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>
Chạy ví dụ »Dự luật 'tên' chuyển tiếp
Trong trường hợp bạn có một số thành phần <Transition>
, nhưng bạn muốn ít nhất một trong các thành phần <Transition>
có hoạt ảnh khác, bạn cần có các tên khác nhau cho các thành phần <Transition>
để phân biệt chúng.
Chúng ta có thể chọn tên của thành phần <Transition>
với name
prop và điều đó cũng thay đổi tên của các lớp chuyển tiếp để chúng ta có thể đặt các quy tắc hoạt ảnh CSS khác nhau cho thành phần đó.
<Transition name="swirl">
Nếu giá trị prop name
chuyển đổi được đặt thành 'swirl' , thì các lớp có sẵn tự động bây giờ sẽ bắt đầu bằng 'swirl-' thay vì 'v-' :
- xoáy -enter-từ
- xoáy -enter-hoạt động
- xoáy -enter-to
- xoáy -rời khỏi
- xoáy -rời khỏi hoạt động
- xoáy -để lại
Trong ví dụ bên dưới, chúng tôi sử dụng name
prop để cung cấp các hoạt ảnh khác nhau cho các thành phần <Transition>
. Một thành phần <Transition>
không được đặt tên và do đó được cung cấp hoạt ảnh bằng cách sử dụng các lớp CSS được tạo tự động bắt đầu bằng 'v-'. Thành phần <Transition>
khác được đặt tên là 'swirl' để có thể đưa ra các quy tắc cho hoạt ảnh với các lớp CSS được tạo tự động bắt đầu bằng 'swirl-'.
Ví dụ
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<p>The second transition in this example has the name prop "swirl", so that we can keep the transitions apart with different class names.</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>
Chạy ví dụ »Móc chuyển tiếp JavaScript
Mỗi lớp Transition như vừa đề cập đều tương ứng với một sự kiện mà chúng ta có thể nối vào để chạy một số mã JavaScript.
Transition Class | JavaScript Event |
---|---|
v-enter-from | before-enter |
v-enter-active | enter |
v-enter-to | after-enter enter-cancelled |
v-leave-from | before-leave |
v-leave-active | leave |
v-leave-to | after-leave leave-cancelled (v-show only) |
Khi sự kiện after-enter
xảy ra trong ví dụ bên dưới, một phương thức sẽ chạy hiển thị phần tử <div>
màu đỏ.
Ví dụ
App.vue
:
<template>
<h1>JavaScript Transition Hooks</h1>
<p>This code hooks into "after-enter" so that after the initial animation is done, a method runs that displays a red 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 the "enter-active" phase of the transition.</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>
Chạy ví dụ » Bạn có thể sử dụng nút "Chuyển đổi" trong ví dụ bên dưới để làm gián đoạn giai đoạn chuyển tiếp nhập của phần tử <p>
để kích hoạt sự kiện enter-cancelled
:
Ví dụ
App.vue
:
<template>
<h1>The 'enter-cancelled' Event</h1>
<p>Click the toggle button again before the enter animation is finished to trigger the 'enter-cancelled' event.</p>
<button @click="pVisible=!pVisible">Toggle</button><br>
<Transition @enter-cancelled="onEnterCancelled">
<p v-if="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-if="divVisible">You interrupted the "enter-active" transition.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
}
},
methods: {
onEnterCancelled() {
this.divVisible = true;
}
}
}
</script>
<style scoped>
.v-enter-active {
animation: swirlAdded 2s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 720deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
</style>
Chạy ví dụ »Dự luật 'xuất hiện'
Nếu chúng ta có một phần tử mà chúng ta muốn tạo hiệu ứng khi tải trang, chúng ta cần sử dụng thuộc tính appear
trên thành phần <Transition>
.
<Transition appear>
...
</Transition>
Trong ví dụ này, phần mở rộng appear
bắt đầu hoạt ảnh khi tải trang lần đầu tiên:
Ví dụ
App.vue
:
<template>
<h1>The 'appear' Prop</h1>
<p>The 'appear' prop starts the animation when the p tag below is rendered for the first time as the page opens. Without the 'appear' prop, this example would have had no animation.</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>
Chạy ví dụ »Chuyển tiếp giữa các yếu tố
Thành phần <Transition>
cũng có thể được sử dụng để chuyển đổi giữa một số thành phần, miễn là chúng ta đảm bảo rằng chỉ có một thành phần được hiển thị tại một thời điểm bằng cách sử dụng <v-if>
và <v-else-if>
:
Ví dụ
App.vue
:
<template>
<h1>Transition Between Elements</h1>
<p>Click the button to get a new image.</p>
<p>The new image is added before the previous is removed. We will fix this in the next example with mode="out-in".</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;
}
img:hover {
cursor: pointer;
}
</style>
Chạy ví dụ »chế độ="ra-vào"
Trong ví dụ trên, hình ảnh tiếp theo được thêm vào trước khi hình ảnh trước đó bị xóa.
Chúng tôi sử dụng giá trị prop và prop mode="out-in"
trên thành phần <Transition>
để việc loại bỏ một phần tử được hoàn tất trước khi phần tử tiếp theo được thêm vào.
Ví dụ
Ngoài mode="out-in"
, ví dụ này cũng sử dụng giá trị được tính toán 'imgActive' thay vì phương thức 'newImg' mà chúng tôi đã sử dụng trong ví dụ trước.
App.vue
:
<template>
<h1>mode="out-in"</h1>
<p>Click the button to get a new image.</p>
<p>With mode="out-in", the next image is not added until the current image is removed. Another difference from the previous example, is that here we use computed prop instead of a method.</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;
}
img:hover {
cursor: pointer;
}
</style>
Chạy ví dụ »Chuyển đổi với các thành phần động
Chúng ta cũng có thể sử dụng thành phần <Transition>
để tạo hiệu ứng chuyển đổi giữa các thành phần động:
Ví dụ
App.vue
:
<template>
<h1>Transition with Dynamic Components</h1>
<p>The Transition component wraps around the dynamic component so that the switching can be animated.</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;
}
}
#app {
width: 350px;
margin: 10px;
}
#app > div {
border: solid black 2px;
padding: 10px;
margin-top: 10px;
}
</style>
Chạy ví dụ »