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