如上图的折叠面板性能你是否有去实现过呢?如果有那你的实现形式是什么样的?遇到了什么问题?
如果没有那就看看我的实现形式吧

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、最初

有了这个组件后想实现折叠面板组件菜单组件树组件等就轻而易举了!