在 Vue 中,<transition>
是一个特殊的包裹组件,用于自动应用过渡效果。当嵌套的元素或组件进入/离开 DOM 时,<transition>
组件将自动检测并应用相应的 CSS 或 JavaScript 过渡/动画。
最简单的使用方式就是在包裹元素或组件时使用 <transition>
标签:
<transition>
<p v-if="show">Hello</p>
</transition>
当你修改 show
属性的值,从 true
切换到 false
或相反,Vue 将自动应用以下 CSS 类:
v-enter-active
-> v-enter
-> v-enter-to
v-leave-active
-> v-leave
-> v-leave-to
你可以在 CSS 中定义这些类:
.v-enter-active, .v-leave-active {
transition: opacity 1s;
}
.v-enter, .v-leave-to {
opacity: 0;
}
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的
v-enter-active 和 v-leave-active 可以控制进入/离开过渡的不同的缓和曲线,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 2 Transition Example</title>
<style>
.v-enter-active, .v-leave-active {
transition: opacity 1s;
}
.v-enter, .v-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
<button @click="toggleShow">Toggle</button>
<transition>
<p v-if="show">Hello</p>
</transition>
</div>
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
show: true
},
methods: {
toggleShow() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
你可以使用 name
属性自定义过渡类的前缀:
<transition name="fade">
<p v-if="show">Hello</p>
</transition>
这将会改变应用的 CSS 类为 fade-enter-active
,fade-enter
,等等。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 2 Transition Example</title>
<style>
/* 使用自定义前缀 "fade" 替代默认的 "v" */
.fade-enter-active, .fade-leave-active {
transition: opacity 1s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
<!-- 点击按钮切换 show 的值,触发过渡效果 -->
<button @click="toggleShow">Toggle</button>
<!-- 使用 name 属性自定义过渡类名的前缀 -->
<transition name="fade">
<p v-if="show">Hello</p>
</transition>
</div>
<!-- 引入 Vue.js 库 -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<!-- Vue 实例 -->
<script>
new Vue({
el: '#app',
data: {
show: true // 控制元素是否显示,从而触发过渡效果
},
methods: {
// 切换 show 的值
toggleShow() {
this.show = !this.show;
}
}
});
</script>
</body>
</html>
Vue 允许你使用 JavaScript 钩子函数来执行更复杂的过渡效果。这些钩子函数可以在 <transition>
标签内通过特定的事件(如 @before-enter
、@enter
等)来触发。
beforeEnter
: 在元素被插入 DOM 之前触发。enter
: 在元素被插入 DOM 后触发。需要调用 done()
函数来通知 Vue 过渡已完成。afterEnter
: 在整个 "enter" 过程结束后触发。beforeLeave
: 在元素开始离开时触发。leave
: 在元素开始离开并被从 DOM 中删除后触发。同样需要调用 done()
函数。afterLeave
: 在整个 "leave" 过程结束后触发。<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
>
<p v-if="show">Hello</p>
</transition>
在 Vue 实例中定义这些方法:
new Vue({
el: '#app',
data: {
show: true
},
methods: {
beforeEnter(el) {
},
enter(el, done) {
done();
},
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue 2 JavaScript Transition Hooks Example</title>
</head>
<body>
<div id="app">
<button @click="toggleShow">Toggle</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show">Hello</p>
</transition>
</div>
<!-- Vue.js -->
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
show: true
},
methods: {
toggleShow() {
this.show = !this.show;
},
beforeEnter(el) {
el.style.opacity = 0;
},
enter(el, done) {
let currentTime = 0;
const interval = 16; // 60 frames per second
const totalTime = 1000; // 1 second
function animate(time) {
currentTime += interval;
const progress = currentTime / totalTime;
if (progress < 1) {
el.style.opacity = progress;
requestAnimationFrame(animate);
} else {
el.style.opacity = 1;
done();
}
}
requestAnimationFrame(animate);
},
leave(el, done) {
let currentTime = 0;
const interval = 16; // 60 frames per second
const totalTime = 1000; // 1 second
function animate(time) {
currentTime += interval;
const progress = 1 - currentTime / totalTime;
if (progress > 0) {
el.style.opacity = progress;
requestAnimationFrame(animate);
} else {
el.style.opacity = 0;
done();
}
}
requestAnimationFrame(animate);
}
}
});
</script>
</body>
</html>
在Vue中,<transition-group>
用于管理多个元素或组件的过渡效果。与<transition>
不同,<transition-group>
不仅可以应用过渡效果于元素进入和离开,还能在排序改变(例如列表排序)时产生过渡效果。
使用<transition-group>
通常需要指定一个tag
属性来决定应该渲染什么类型的DOM元素来包裹子元素。默认为<span>
。
下面是一个基础示例:
<!DOCTYPE html>
<html>
<head>
<title>Vue 2 Transition Group Example</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<style>
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
</style>
</head>
<body>
<div id="app">
<button @click="addItem">Add Item</button>
<button @click="removeItem">Remove Item</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</transition-group>
</div>
<script>
new Vue({
el: '#app',
data: {
items: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' }
],
nextId: 4
},
methods: {
addItem() {
this.items.push({ id: this.nextId++, text: `Item ${this.nextId}` });
},
removeItem() {
this.items.length > 0 && this.items.pop();
}
}
});
</script>
</body>
</html>
在这个例子中:
key
属性,这对于<transition-group>
来说是必需的。list
)与<transition-group>
的name
属性值相匹配。点击“Add Item”和“Remove Item”按钮,你会看到列表项有平滑的过渡效果。
使用<transition-group>
,你可以很容易地创建诸如排序、添加或删除的复杂过渡效果,这在普通的<transition>
组件中要更复杂地实现。
在 Vue 中,CSS 动画用于在元素进入或离开 DOM 时添加动画效果。这些效果通过使用专门的 CSS 类和 Vue 的 <transition>
组件进行管理。
CSS 动画的基本示例
下面是一个使用 CSS 动画的简单示例:
<!DOCTYPE html>
<html>
<head>
<title>Vue CSS Animation</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<style>
@keyframes slide-in {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slide-out {
from {
transform: translateX(0);
}
to {
transform: translateX(100%);
}
}
.slide-enter-active, .slide-leave-active {
animation-duration: 1s;
}
.slide-enter-active {
animation-name: slide-in;
}
.slide-leave-active {
animation-name: slide-out;
}
</style>
</head>
<body>
<div id="app">
<button @click="show = !show">Toggle</button>
<transition name="slide">
<div v-if="show">Hello, World!</div>
</transition>
</div>
<script>
new Vue({
el: '#app',
data: {
show: true
}
});
</script>
</body>
</html>
在这个示例中:
@keyframes
定义的。slide-enter-active
和 slide-leave-active
类用于指定动画的名称和持续时间。<transition name="slide">
对应于 CSS 类的前缀(这里是 "slide")。当你点击 "Toggle" 按钮时,"Hello, World!" 的 div 元素会滑入和滑出。
说明
slide-enter-active
类在元素正在进入时应用,并保持应用直到过渡完成。slide-leave-active
类在元素开始离开时应用,并保持应用直到过渡完成。CSS 过渡(CSS Transitions)是一种简单的方式,用于在两个不同的状态间添加动画效果。它们主要用于在鼠标悬停、焦点或激活某个元素,或在修改 DOM 时创建平滑的动画。
基础语法
CSS 过渡的基础语法如下:
.selector {
/* 初始状态 */
background-color: blue;
transition: background-color 0.3s ease;
}
.selector:hover {
/* 目标状态 */
background-color: red;
}
这里的 .selector
代表你希望添加过渡效果的 CSS 选择器。在这个示例中,当你悬停在 .selector
元素上时,background-color
将在 0.3 秒内从蓝色平滑过渡到红色。
过渡属性
transition-property
: 需要添加过渡效果的 CSS 属性名称。transition-duration
: 过渡动画的持续时间。transition-timing-function
: 过渡动画的速度曲线。transition-delay
: 过渡动画开始前的延迟时间。示例代码与 Vue 2 集成
在 Vue.js 中,你可以使用 <transition>
组件与 CSS 过渡相结合:
<!DOCTYPE html>
<html>
<head>
<title>CSS Transition with Vue</title>
<style>
/* 初始状态 */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
/* 进入与离开过渡前状态 */
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app">
<button @click="show = !show">Toggle</button>
<transition name="fade">
<p v-if="show">Hello</p>
</transition>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
new Vue({
el: '#app',
data: {
show: true
}
});
</script>
</body>
</html>
在这个示例中,当你点击 "Toggle" 按钮时,文本 "Hello" 的透明度会在 0.5 秒内从 0 到 1(或从 1 到 0)平滑过渡。这是通过 .fade-enter-active
和 .fade-leave-active
类,以及 transition: opacity 0.5s;
实现的。
transitionend
是一个 JavaScript 事件,它在 CSS 过渡完成后触发。这个事件允许你在 CSS 过渡结束时执行某些动作或逻辑。
基础用法
下面是一个简单的 HTML 和 JavaScript 示例,展示了如何使用 transitionend
事件。
<!DOCTYPE html>
<html>
<head>
<title>transitionend Example</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: blue;
transition: width 2s;
}
</style>
</head>
<body>
<div id="box"></div>
<button id="animate">Animate</button>
<script>
const box = document.getElementById('box');
const animateBtn = document.getElementById('animate');
box.addEventListener('transitionend', function() {
alert('Transition has ended!');
});
animateBtn.addEventListener('click', function() {
box.style.width = '200px';
});
</script>
</body>
</html>
在这个示例中,当你点击 "Animate" 按钮时,#box
的宽度会在 2 秒内从 100px 变为 200px。过渡完成后,会触发 transitionend
事件,并弹出一个警告框显示 "Transition has ended!"。
注意事项
多属性过渡: 如果你对多个属性应用过渡,transitionend
事件可能会多次触发。你可以通过事件对象的 propertyName
属性来确定是哪个属性的过渡结束了。
box.addEventListener('transitionend', function(event) {
console.log(event.propertyName + ' has finished transitioning.');
});
兼容性: 虽然大多数现代浏览器都支持 transitionend
事件,但最好还是检查兼容性并考虑使用前缀,比如 webkitTransitionEnd
。
取消过渡: 如果过渡被取消(例如,元素突然不可见或过渡属性被覆盖),transitionend
事件可能不会触发。
在 Vue 中使用 transitionend
在 Vue.js 中,你可以使用原生 DOM 事件绑定方式在 transition
组件上使用 transitionend
,如下:
<transition @transitionend="handleTransitionEnd">
<p v-if="show">Hello</p>
</transition>
然后,在 Vue 实例的 methods 中定义 handleTransitionEnd
方法:
new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleTransitionEnd() {
console.log('Transition has ended');
}
}
});
这样,每当过渡结束时,handleTransitionEnd
方法就会被调用,你就可以在这里执行任何需要在过渡结束后进行的操作。
CSS @keyframes
动画用于创建复杂的动画序列,这些动画不仅限于从一个样式迁移到另一个样式,还可以包括多个中间步骤。
基础语法
创建一个 @keyframes
动画涉及定义动画的名称和各个阶段的样式。
@keyframes myAnimation {
0% {
background-color: red;
left: 0px;
}
50% {
background-color: yellow;
left: 50px;
}
100% {
background-color: green;
left: 100px;
}
}
上面的 @keyframes
动画定义了一个名为 myAnimation
的动画,其中包含了三个阶段(0%, 50%, 和 100%)。
然后你需要把这个 @keyframes
动画绑定到一个或多个元素上,通常使用 animation
属性。
#box {
animation: myAnimation 4s infinite;
}
动画属性
animation-name
: 动画的名称(即 @keyframes
的名称)。animation-duration
: 动画完成的时间。animation-timing-function
: 定义速度曲线的动画。animation-delay
: 动画开始前的延迟。animation-iteration-count
: 动画应该播放的次数。animation-direction
: 动画是否在每个周期结束时反向播放。这些属性也可以被组合为一个 animation
属性。
示例
HTML:
<!DOCTYPE html>
<html>
<head>
<title>@keyframes Example</title>
<style>
#box {
width: 100px;
height: 100px;
position: relative;
animation: myAnimation 4s infinite;
}
@keyframes myAnimation {
0% {
background-color: red;
left: 0px;
}
50% {
background-color: yellow;
left: 50px;
}
100% {
background-color: green;
left: 100px;
}
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
这个例子中,元素 #box
会进行一个 4 秒的无限循环动画,该动画按照 @keyframes myAnimation
的定义来变更其 background-color
和 left
属性。
animationend
是一个 DOM 事件,当 CSS 动画完成一个完整的周期后触发。这个事件可以用于检测动画何时结束,并执行一些后续操作,如移除动画类,改变元素状态,或启动另一个动画等。
这个事件在动画周期完成后触发,而不是每个动画迭代完成后触发。如果你的动画设置了无限循环(例如,animation-iteration-count: infinite;
),那么 animationend
事件将不会被触发。
如何使用
你可以使用 JavaScript 的 addEventListener
方法来监听 animationend
事件。
// 使用 JavaScript 绑定 animationend 事件
document.getElementById("myElement").addEventListener("animationend", function(event) {
// 动画结束后要执行的代码
alert("Animation ended!");
});
这里是一个简单的示例:
HTML:
<!DOCTYPE html>
<html>
<head>
<title>animationend Example</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: red;
animation: myAnimation 2s;
}
@keyframes myAnimation {
0% { background-color: red; }
100% { background-color: green; }
}
</style>
</head>
<body>
<div id="box"></div>
<script>
document.getElementById("box").addEventListener("animationend", function(event) {
alert("Animation ended!");
});
</script>
</body>
</html>
在这个例子中,当 #box
的动画结束时,会触发 animationend
事件,然后显示一个 alert 对话框。
注意
animationend
事件是区分大小写的,确保所有的字母都是小写。webkitAnimationEnd
。使用 animationend
事件,你可以更精确地控制动画的行为,并在动画结束后执行特定的操作。
FLIP 是一种用于创建高性能动画的技术。该名字是首字母缩写,分别代表以下四个步骤:First(第一步)、Last(最后一步)、Invert(反转)、Play(播放)。
工作原理
First(第一步): 在动画开始之前,获取元素的当前位置和尺寸。
Last(最后一步): 然后,应用任何会导致元素移动或者改变尺寸的 CSS 规则,并且重新获取该元素的最终位置和尺寸。
Invert(反转): 计算第一步和第二步之间的差异(位置、尺寸等),并使用 CSS transform 反转这些更改。这意味着,你将元素移动回它原来的位置和尺寸。
Play(播放): 最后,使用 CSS transition 将 transform 属性设置回 "none",这样元素就会回到它的最终状态。
通过这种方式,你实际上是在使用 transform 和 opacity 来创建动画,这些属性不会触发浏览器的布局或重绘,从而能够提供更流畅的动画效果。
示例代码
以下是一个简单的 FLIP 动画示例,该示例在一个元素从一个位置平滑移动到另一个位置:
<!DOCTYPE html>
<html>
<head>
<style>
#box {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
function moveElement() {
// First: 获取元素初始位置
const firstRect = box.getBoundingClientRect();
// Last: 移动元素
box.style.left = "400px";
box.style.top = "300px";
// 触发重绘以获取最终位置
box.offsetHeight;
// 获取元素最终位置
const lastRect = box.getBoundingClientRect();
// Invert: 反转元素位置
const deltaX = firstRect.left - lastRect.left;
const deltaY = firstRect.top - lastRect.top;
box.style.transition = "none";
box.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
// 触发重绘以应用反转
box.offsetHeight;
// Play: 播放动画
box.style.transition = "transform 0.5s";
box.style.transform = "";
}
// 延迟以便页面加载完成
setTimeout(moveElement, 500);
</script>
</body>
</html>
在这个例子中,我们首先获取元素的初始位置(First),然后更改元素的位置(Last)。接下来,我们计算两者之间的差异并反转这些更改(Invert)。最后,我们应用一个 transition 属性,并将 transform 属性设置为 "none"(Play)。
使用 FLIP,你可以创建复杂的动画效果,而不会牺牲性能。这是一个在前端开发中非常有用的技术。