vue动画过渡 javascript钩子函数详解
vue动画过渡js钩子函数详解
前言
本文中,enter(leave)过程指的是beforeEnter、enter、afterEnter(beforeLeave、leave、afterLeave)三个阶段。
阅读过程请着重看js部分。
js钩子函数运行时间
js钩子函数的运行时机分三种情况:
- 不显示指定参数done:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>动画和过渡</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> #show { text-align: center; overflow: hidden; position: relative; } p { display: block; width: 100px; margin: 100px auto; border: 1px solid black; } </style> </head> <body> <div id="app"> <div id="show"> <button @click="show=!show">点击</button> <!-- 使用javascript钩子函数 --> <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave"> <p v-if="show">测试钩子函数</p> </transition> </div> </div> <script> const app = new Vue({ el: '#app', data: { show: true, }, methods: { beforeEnter(el) { console.log(1); }, enter(el) { console.log(2); }, afterEnter(el) { console.log(3); }, beforeLeave(el) { console.log(4) }, leave(el) { console.log(5); }, afterLeave(el) { console.log(6); } }, }) </script>
运行结果:
可以看出,第一次点击进入leave过程,输出4、5、6(6稍停顿一会),p节点消失。再点击按钮,输出1、2、3,p节点出现。
- 显示指定done参数,但不调用:
<script>
const app = new Vue({
el: '#app',
data: {
show: true,
},
methods: {
beforeEnter(el) {
console.log(1);
},
enter(el, done) {
console.log(2);
// done() 不调用
},
afterEnter(el) {
console.log(3);
},
beforeLeave(el) {
console.log(4)
},
leave(el, done) {
console.log(5);
// done()不调用
},
afterLeave(el) {
console.log(6);
}
},
})
</script>
运行结果:
第一次点击按钮输出4、5,再次点击按钮输出1、6、2。注意这是p节点始终不消失,并且没有输出3(即afterEnter函数没有运行)
- 显示指定done参数,并且调用:
<script>
const app = new Vue({
el: '#app',
data: {
show: true,
},
methods: {
beforeEnter(el) {
console.log(1);
},
enter(el, done) {
console.log(2);
done()
},
afterEnter(el) {
console.log(3);
},
beforeLeave(el) {
console.log(4)
},
leave(el, done) {
console.log(5);
done()
},
afterLeave(el) {
console.log(6);
}
},
})
</script>
运行结果:
第一次点击输出4、5、6 (6没有任何停顿),p节点消失。再次点击按钮输出1、2、3,p节点出现。
出项以上情况是因为,vue在定义js钩子函数时,会判断enter和leave钩子函数的参数个数,如果没有显示指定done参数,vue会自动判断动画或过渡的结束时间,并且执行afterEnter函数或afterLeave函数,此时在enter(leave)过程,beforeEnter、enter和afterEnter(beforeLeave、leave和afterLeave)是顺序执行的,元素节点也会紧跟着出现(消失)。vue自动判断动画或过渡效果结束时间是有些许滞后的,会导致afterEnter钩子函数和afterLeave钩子函数调用时间有所延迟(这就是为什么‘6’输出稍有停顿)。
ps: enter钩子函数也没有调用done,但是‘3’似乎并没有滞后输出或者延迟的不明显。
如果显示指定done参数,则表示手动决定动画或过渡的结束时间,调用done就表示动画或过渡结束,然后执行afterEnter函数或afterLeave函数,在之后节点消失或出现。
如果不调用done,vue会认为动画或过渡一直未结束,就不会有afterEnter函数或afterLeave函数的运行,节点也一直处于enter或leave阶段,所以节点并不会消失。但是当节点一直处于leave阶段时,再次点击了按钮使节点位于enter过程,vue就会自动结束上一个leave过程,执行afterLeave钩子函数。这也就是为什么在上面第三种情况中,‘6’会出现在‘1’之后。
js钩子函数的过渡或动画
vue教程中的原话:
这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。
1.当你利用 CSS transitions/animation实现动画过渡效果时,需要注意:
- 未渲染的节点 或 display为none的节点 本身是没法触发动画和过渡效果的。
- 在leave过程,如果调用done(),节点会很快消失,过渡效果就看不到了。
我们看一段代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动画和过渡</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#show {
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<div id="show">
<button @click="show=!show">点击</button>
<!-- 使用javascript钩子函数 -->
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @before-leave="beforeLeave"
@leave="leave" @after-leave="afterLeave">
<p v-if="show">测试钩子函数</p>
</transition>
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
show: true,
},
methods: {
beforeEnter(el) {
el.style.opacity = '0';
el.style.transition ='all 1s';
},
enter(el) {
el.style.opacity = '1';
},
afterEnter(el) {
},
beforeLeave(el) {
el.style.transition ='all 1s';
el.style.opacity = '1';
},
leave(el,done) {
el.style.opacity = '0';
done()
},
afterLeave(el) {
}
},
})
</script>
</body>
</html>
运行上面的代码你会发现,没有任何的动画过渡效果。对于上面第一个问题,你需要了解浏览器的渲染机制。浏览器并不是实时渲染的。而是处理完所有代码或者遇到一些特定代码才会引起浏览器的重排或者重绘。对与enter过程,执行完beforeEnter函数后,在浏览器页面中并没有为p节点加上transition和opacity属性,因为还有代码未处理完,直到之后enter函数运行完才会进行重绘过程。为元素节点加上transition和opacity=1(opacity=0被覆盖)属性。所以,只需要在enter钩子函数的 el.style.opacity = ‘0’ 前加入特定代码引起浏览器重绘或重排即可.
关于重排和重回可以看看 https://segmentfault.com/a/1190000016990089
对于第二个问题,手动调用done()函数,会导致vue认为过渡效果结束,让p节点迅速消失。可以让vue自动判断动画过渡效果的结束时间。
以下是解决方案:
<script>
const app = new Vue({
el: '#app',
data: {
show: true,
},
methods: {
beforeEnter(el) {
el.style.opacity = '0';
el.style.transition ='all 1s';
},
enter(el) {
//el.offsetWidth引起浏览器的重回
el.offsetWidth
el.style.opacity = '1';
},
afterEnter(el) {
},
beforeLeave(el) {
el.style.transition ='all 1s';
el.style.opacity = '1';
},
leave(el) {
//不显示定义done参数,让vue自动判断动画结束时机
el.style.opacity = '0';
},
afterLeave(el) {
}
},
})
</script>
运行效果:
2.单独使用js钩子函数实现动画效果:
理解了上面js钩子函数的运行机制,单独使用就不难了。需要留意vue官方教程中的原提醒:
当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
下面是一个实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动画和过渡</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#show {
text-align: center;
overflow: hidden;
position: relative;
}
p {
display: block;
width: 100px;
margin: 100px auto;
border: 1px solid black;
}
</style>
</head>
<body>
<div id="app">
<div id="show">
<button @click="show=!show">点击</button>
<!-- 使用javascript钩子函数 -->
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @before-leave="beforeLeave"
@leave="leave" @after-leave="afterLeave">
<p v-if="show">测试钩子函数</p>
</transition>
</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
show: true,
},
methods: {
beforeEnter(el) {
console.log(1);
el.style.position = 'relative';
},
enter(el, done) {
console.log(2);
clearInterval(this.leaveTimer);
this.enterTimer = setInterval(() => {
let style = document.defaultView.getComputedStyle(el, null);
let x = parseInt(style.left);
console.log('enter');
el.style.left = (x + 10) + 'px';
}, 1000);
done();
},
afterEnter(el) {
console.log(3);
},
beforeLeave(el) {
el.style.position = 'relative';
console.log(4)
},
leave(el, done) {
console.log(5);
clearInterval(this.enterTimer);
let count = 1;
this.leaveTimer = setInterval(() => {
let style = document.defaultView.getComputedStyle(el, null);
let x = parseInt(style.left);
console.log('leave');
el.style.left = (x - 10) + 'px';
}, 1000);
},
afterLeave(el) {
console.log(6);
}
},
})
</script>
</body>
</html>
运行效果:
如果,上面代码enter 和 leave函数中都不指定done参数,那么就看不到离开的动画效果了。因为过渡效果将会立即完成。
总结
-
done参数用于指定动画或过渡的结束时机。如果不指定done参数,表示由vue自动判断动画或者过渡的结束时机。但是此时,会导致元素在enter过程,beforeEnter,enter,afterEnter顺序执行而且元素是瞬间渲染出来的。同样的在leave过程,beforeleave,leave,afterleave会顺序执行并且元素会快速消失,leave阶段的过渡动画效果不生效,因为此时元素已经移除了。
-
如果指定done参数,调用done()表示的是动画或过渡效果结束,之后会执行afterEnter或afterLeave。
-
如果指定done参数不调用done,元素将一直处于enter(leave)阶段。若v-if状态一直不变,el元素一直不会移除。如果调用done,元素会立即消失。
结语
第一次写博客,以上皆为个人理解,若有误,敬请谅解, 感谢!
本文地址:https://blog.csdn.net/weixin_43375997/article/details/109615311