element-plus中的折叠效果
在业务开发中时常会遇到面板折叠的情况,通常会借用组件库中的组件来实现折叠效果。以element-plus为例,通常会使用其折叠面板来实现功能,但是通用组件可能没办法满足日常开发中的定制化业务需求,有时候只需要折叠的动画效果,这时候就可以参考组件源码实现丝滑的折叠效果。
vue3提供了两个内置组件来处理DOM变化时候的过渡动画Transition和TransitionGroup,具体使用方法可以参考官网。在了解Transition组件之后可以参考element-plus中折叠面板的源码来实现丝滑的折叠效果。
实现
collapse组件中的折叠效果使用transition组件实现,主要为以下部分
- 模版部分,模版使用transition组件
<template>
<transition name="collapse-transition" v-on="on">
<slot />
</transition>
</template>
- js部分,使用了requestAnimationFrame() 方法来执行动画,它会在dom更新的时候执行,有关于requestAnimationFrame方法的使用,可以参考文档
// js部分
// 收起时的事件,还原dom原本的样式
const reset = (el) => {
el.style.maxHeight = '';
el.style.overflow = el.dataset.oldOverflow;
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
};
// 监听Transition组件的回调事件
const on = {
// 监听元素插入之前,也就是动画的起始状态
// 此处记录元素的初始状态,以便于折叠的时候还原DOM
beforeEnter(el) {
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop; // 记录DOM原本padding样式
el.dataset.oldPaddingBottom = el.style.paddingBottom;
if (el.style.height) el.dataset.elExistsHeight = el.style.height; // 记录DOM原本height
// 记录到原本样式后,将其置为0,元素处于收起状态
el.style.maxHeight = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
},
// 监听enter事件,在元素即将要被插入的时候,调用requestAnimationFrame方法执行动画,
enter(el) {
requestAnimationFrame(() => {
// 记录元素原本的overflow值,在动画执行的时候需要将其置为hidden,防止在动画执行的时候元素高度变化引起元素内容溢出
el.dataset.oldOverflow = el.style.overflow;
// 通过改变元素的高度,将元素展开
if (el.dataset.elExistsHeight) {
el.style.maxHeight = el.dataset.elExistsHeight;
} else if (el.scrollHeight !== 0) {
el.style.maxHeight = `${el.scrollHeight}px`;
} else {
el.style.maxHeight = 0;
}
// 同时处理元素的padding和overflow属性防止内容溢出
el.style.paddingTop = el.dataset.oldPaddingTop;
el.style.paddingBottom = el.dataset.oldPaddingBottom;
el.style.overflow = 'hidden';
});
},
// 元素插入之后,还原状态
afterEnter(el) {
el.style.maxHeight = '';
el.style.overflow = el.dataset.oldOverflow;
},
// 监听在动画执行中,取消执行,需要将其还原
enterCancelled(el) {
reset(el);
},
// 离开动画前,记录当前元素的属性
beforeLeave(el) {
if (!el.dataset) el.dataset = {};
el.dataset.oldPaddingTop = el.style.paddingTop;
el.dataset.oldPaddingBottom = el.style.paddingBottom;
el.dataset.oldOverflow = el.style.overflow;
el.style.maxHeight = `${el.scrollHeight}px`;
el.style.overflow = 'hidden';
},
// 离开动画的时候,将其设为0,元素折叠
leave(el) {
if (el.scrollHeight !== 0) {
el.style.maxHeight = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
}
},
// 当元素移除完成之后还原属性
afterLeave(el) {
reset(el);
},
// 取消折叠的时候还原属性
leaveCancelled(el) {
reset(el);
},
};
- css动画部分,transition组件会根据绑定的name值设置组件的类名,在展开折叠的时候根据类名的变化来设置组件的css过渡动画
.collapse-transition {
transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
}
.collapse-transition-leave-active,
.collapse-transition-enter-active {
transition: 0.3s max-height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
}
结尾
在业务中使用时,可以参照此方法来实现丝滑的展开折叠效果,左右折叠也可以通过修改元素的宽度实现