如何实现手风琴组件高度自适应 + 高度动画
1 功能需求
1.1 打开合上面板的动画
// template
<div
:style="{ height: height + 'rem' }"
>...</div>
// style
.animation {
...
transition: height 0.5s
}
// js
initHeight = 1
maxHeight = 2 // 经过复杂的js预计算获得,不好维护
height = initHeight
open = false
function changeHeight() {
open = !open
this.height = open ? maxHeight : initHeight
}
1.2 高度自适应,避免js计算
// template
<div
:style="divStyle"
>...</div>
// style
.animation {
...
transition: height 0.5s
}
// js
initHeight = 1
open = false
get divStyle() {
return open ? {} : { height: initHeight }
}
function changeHeight() {
open = !open
}
2 功能冲突
自适应布局时height为auto,所以能自动根据content撑开,但是于此同时,也就破坏了height动画,因为height的from和to必须为某一定值而不是auto
3 解决方案
3.1 方案1 使用maxHeight(bad)
// js
maxHeight = 999
open = false
get divStyle() {
return open ? { maxHeight } : { height: initHeight }
}
function changeHeight() {
open = !open
}
这种方案的原理是通过设置足够大的maxHeight来实现动画的,缺点是打开状态下由于maxHeight比实际的height大得多,因此在动画的前半段时间,高度动画是卡顿住的,无法与盒子内部content的动画一致,体验不好。
如果要改善这个问题,只能通过不断减小maxHeight的值;但与此同时,当maxHeight足够小是,为了保证内容不被hidden,我们又要绕回到js计算最高高度了。所以这个方案不可行。
3.2 方案2 初始化时将盒子设置为展开状态,记住最高高度然后还原展开状态(good)
// js
const currentOpen = this.operationIsOpen;
this.operationIsOpen = true;
this.$nextTick(() => {
const operationBlock = document.getElementsByClassName(
'operation-block'
)[0];
if (!operationBlock) return;
this.maxHeight = operationBlock.offsetHeight;
this.operationIsOpen = currentOpen;
});
获取了准确的maxHeight而又不用进行js计算,个人认为这是这个需求的最优解