纯 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 是怎么实现手风琴的,用到了渲染函数。