element-plus中的折叠效果

767 阅读2分钟

element-plus中的折叠效果

在业务开发中时常会遇到面板折叠的情况,通常会借用组件库中的组件来实现折叠效果。以element-plus为例,通常会使用其折叠面板来实现功能,但是通用组件可能没办法满足日常开发中的定制化业务需求,有时候只需要折叠的动画效果,这时候就可以参考组件源码实现丝滑的折叠效果。

vue3提供了两个内置组件来处理DOM变化时候的过渡动画TransitionTransitionGroup,具体使用方法可以参考官网。在了解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;
}

结尾

在业务中使用时,可以参照此方法来实现丝滑的展开折叠效果,左右折叠也可以通过修改元素的宽度实现