如上图的折叠面板性能你是否有去实现过呢?如果有那你的实现形式是什么样的?遇到了什么问题?
如果没有那就看看我的实现形式吧
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、最初
有了这个组件后想实现 折叠面板组件
、 菜单组件
、 树组件
等就轻而易举了!