纯 CSS 的问题

当手风琴内容固定的时候,使用特定的 active class 配合 transition 即可在 CSS 中完成手风琴布局,大概长这样:

.content{
	height:0;
	transition:height 0.2s ease;/*动画*/
}
.active{
	height:100px;/*事先确定的高度*/
}

然而作为一个通用的手风琴组件,不可能事先确定高度,最后还是要依赖JS。

原生 JS 的方法

通过js,我们可以获得伸缩容器内的原始高度 clientHeight(注意此时设置的 box-sizing 应该是 border-box 才能和 element.style.height 对应)。

<div id="trigger">trigger</div>
<div id="content" style="height:0;">
	<p>
		balabala
	</p>
</div>
window.onload = function(){
	document.getElementById('trigger').addEventListener('click', function(){
		let target = document.getElementById('content');
		target.style.height = target.clientHeight + 'px';//恢复高度
	});
}

vue 组件的方法

用vue实现!把动画也加上!
类似上面的思路,移植到 vue 中就行了。分两种思路:一种是将 trigger 和 content 都设置成slot,整个组件对于调用者来说只是嵌套了一层。另一种是将内容传入该组件,content 的展示逻辑需要写在手风琴布局中,耦合度高一点。只展示第二种,slot的方法同理。

动画使用 TWEEN 这个方便实现插值的库,具体看代码。

在vue中,可以通过this.$children获取到该组件内部的子组件,再通过$el获取DOM节点元素。

<div id="Collapse">
	<span class="select">
		<slot name="selector"></slot>
	</span>
	<span class="trigger" @click="transActive">
		<i class="iconfont icon-more"></i>
	</span>
	<div id="content">
		<component v-if="transferItem" :is="transferComp" :item="transferItem"></component>
	</div>
</div>
methods: {
    transActive: function() {
      let th = 0;
	  /*-----获取长度-----*/
      if (this.isActive) {
        th = this.$el.clientHeight - this.$children[0].$el.clientHeight;
      } else {
        th = this.$el.clientHeight + this.$children[0].$el.clientHeight;
      }
	  /*-----设置动画-----*/
      let tween = new TWEEN.Tween(this).to({ contentHeight: th }, 200);
      tween.easing(TWEEN.Easing.Cubic.InOut);
      tween.start();
      tween.onUpdate(function(t) {
        console.log(t);
      });
	  /*-----触发动画-----*/
      animate();
      function animate() {
        requestAnimationFrame(animate);
        TWEEN.update();
      }
      this.isActive = !this.isActive;
    }
},
watch:{
	contentHeight:function(){
		this.$el.style.height = this.contentHeight + "px";
		//实际绑定到元素上的高度
	}
}

这样就实现了可自适应内容高度、带展开动画的手风琴组件。高端玩家可以看看 element-ui 是怎么实现手风琴的,用到了渲染函数。