共计 4618 个字符,预计需要花费 12 分钟才能阅读完成。
如上图的折叠面板性能你是否有去实现过呢?如果有那你的实现形式是什么样的?遇到了什么问题?
如果没有那就看看我的实现形式吧
1、最大的难题
这种折叠成果理论管制的是元素的高度,css、js 管制元素高度的属性有 height、max-height。
既然是通过 height 来实现,那很简略,我间接通过款式来解决,如下:
.collapse-transition-enter-from, | |
.collapse-transition-leave-to{height: 0;} | |
.collapse-transition-enter-active, | |
.collapse-transition-leave-active{ | |
overflow: hidden; | |
transition: height .3s ease-in-out, padding .3s ease-in-out, max-height .3s ease-in-out; | |
} | |
.collapse-transition-enter-to, | |
.collapse-transition-leave-from{height: auto;} |
自信的写完上述代码并运行后你会发现,这并任何没有过渡成果,并且元素在暗藏的时候还有点卡顿的感觉
这是为什么呢? 起因是:transition 对 height: auto 有效,height 必须设置除 auto 外的有效值才会失效
那么问题来了,该什么时候获取并设置元素的高度?什么时候该复原元素的高度?
答案是:
- 1、进入时,过渡成果进入后获取并设置元素的高度,进入过渡成果实现后立刻复原
- 2、来到时,过渡成果来到前获取并设置元素的高度,来到过渡成果实现后立刻复原
这里波及到了 4 个机会,因而光靠 css 是实现不了的,还需依附<transition>
组件的钩子
2、残缺代码
<template> | |
<transition | |
:name="transitionName" | |
@before-enter="collapseBeforeEnter" | |
@enter="collapseEnter" | |
@after-enter="collapseAfterEnter" | |
@before-leave="collapseBeforeLeave" | |
@leave="collapseLeave" | |
@after-leave="collapseAfterLeave"> | |
<slot></slot> | |
</transition> | |
</template> | |
<script> | |
/** | |
* 元素折叠适度成果 | |
*/ | |
export default { | |
name: 'CollapseTransition', | |
props: { | |
transitionName: { | |
type: String, | |
default: 'collapse-transition' | |
} | |
}, | |
data () { | |
return { | |
oldPaddingTop: '', | |
oldPaddingBottom: '', | |
oldOverflow: '' | |
} | |
}, | |
methods: {collapseBeforeEnter (el) {// console.log('11, collapseBeforeEnter'); | |
this.oldPaddingBottom = el.style.paddingBottom; | |
this.oldPaddingTop = el.style.paddingTop; | |
// 过渡成果开始前设置元素的 maxHeight 为 0,让元素 maxHeight 有一个初始值 | |
el.style.paddingTop = '0'; | |
el.style.paddingBottom = '0'; | |
el.style.maxHeight = '0'; | |
}, | |
collapseEnter (el, done) {// console.log('22, collapseEnter'); | |
// | |
this.oldOverflow = el.style.overflow; | |
let elHeight = el.scrollHeight; | |
// 过渡成果进入后将元素的 maxHeight 设置为元素自身的高度,将元素 maxHeight 设置为 auto 不会有过渡成果 | |
if (elHeight > 0) {el.style.maxHeight = elHeight + 'px';} else {el.style.maxHeight = '0';} | |
el.style.paddingTop = this.oldPaddingTop; | |
el.style.paddingBottom = this.oldPaddingBottom; | |
el.style.overflow = 'hidden'; | |
// done(); | |
let onTransitionDone = function () {done(); | |
// console.log('enter onTransitionDone'); | |
el.removeEventListener('transitionend', onTransitionDone, false); | |
el.removeEventListener('transitioncancel', onTransitionDone, false); | |
}; | |
// 绑定元素的 transition 实现事件,在 transition 实现后立刻实现 vue 的适度动效 | |
el.addEventListener('transitionend', onTransitionDone, false); | |
el.addEventListener('transitioncancel', onTransitionDone, false); | |
}, | |
collapseAfterEnter (el) {// console.log('33, collapseAfterEnter'); | |
// 过渡成果实现后复原元素的 maxHeight | |
el.style.maxHeight = ''; | |
el.style.overflow = this.oldOverflow; | |
}, | |
collapseBeforeLeave (el) {// console.log('44, collapseBeforeLeave', el.scrollHeight); | |
this.oldPaddingBottom = el.style.paddingBottom; | |
this.oldPaddingTop = el.style.paddingTop; | |
this.oldOverflow = el.style.overflow; | |
el.style.maxHeight = el.scrollHeight + 'px'; | |
el.style.overflow = 'hidden'; | |
}, | |
collapseLeave (el, done) {// console.log('55, collapseLeave', el.scrollHeight); | |
if (el.scrollHeight !== 0) { | |
el.style.maxHeight = '0'; | |
el.style.paddingBottom = '0'; | |
el.style.paddingTop = '0'; | |
} | |
// done(); | |
let onTransitionDone = function () {done(); | |
// console.log('leave onTransitionDone'); | |
el.removeEventListener('transitionend', onTransitionDone, false); | |
el.removeEventListener('transitioncancel', onTransitionDone, false); | |
}; | |
// 绑定元素的 transition 实现事件,在 transition 实现后立刻实现 vue 的适度动效 | |
el.addEventListener('transitionend', onTransitionDone, false); | |
el.addEventListener('transitioncancel', onTransitionDone, false); | |
}, | |
collapseAfterLeave (el) {// console.log('66, collapseAfterLeave'); | |
el.style.maxHeight = ''; | |
el.style.overflow = this.oldOverflow; | |
el.style.paddingBottom = this.oldPaddingBottom; | |
el.style.paddingTop = this.oldPaddingTop; | |
this.oldOverflow = this.oldPaddingBottom = this.oldPaddingTop = ''; | |
} | |
} | |
}; | |
</script> | |
<style lang="less"> | |
.collapse-transition-enter-active, | |
.collapse-transition-leave-active{transition: height .3s ease-in-out, padding .3s ease-in-out, max-height .3s ease-in-out;} | |
</style> |
看完代码后你可能会奇怪为什么是设置元素的 max-height
而不是元素的 height
?
这么做的起因是:不论元素自身有没有通过 style
设置height
,都不会影响元素高度的计算
3、应用组件
<template> | |
<div class="home"> | |
<div style="padding: 50px;"> | |
<div style="margin-bottom: 10px;"> | |
<button type="button" @click="toggleExpand"> 开展 / 折叠 </button> | |
</div> | |
<CollapseTransition> | |
<div class="description" v-show="expanded"> | |
<dl> | |
<dt> 操作账号:</dt><dd>super_admin</dd> | |
</dl> | |
<dl> | |
<dt> 操作模块:</dt><dd> 日志模块 </dd> | |
</dl> | |
<dl> | |
<dt> 操作形容:</dt><dd> 查看系统日志 </dd> | |
</dl> | |
<dl> | |
<dt> 操作 IP:</dt><dd>192.168.1.100</dd> | |
</dl> | |
</div> | |
</CollapseTransition> | |
</div> | |
</div> | |
</template> | |
<script> | |
import CollapseTransition from './CollapseTransition' | |
export default { | |
name: 'home', | |
components: {CollapseTransition}, | |
data() { | |
return {expanded: false} | |
}, | |
methods: {toggleExpand () {this.expanded = !this.expanded;} | |
} | |
} | |
</script> | |
<style type="text/css" lang="less" scoped> | |
.description{ | |
padding: 15px; | |
width: 600px; | |
border: 1px solid #3a8ee6; | |
dl{margin-bottom: 5px;} | |
dt{display: block; float: left;} | |
dd{display: block; overflow: hidden;} | |
} | |
</style> |
最终成果:
4、最初
有了这个组件后想实现 折叠面板组件
、 菜单组件
、 树组件
等就轻而易举了!
正文完