【vue3组件篇】任意高度的内容面板显隐添加过渡动画

问题难点

任意高度的内容面板展示/隐藏过程中如何添加过渡动画效果?


大家好,我是webber老丁。相信很多朋友在vue开发一些折叠功能(如菜单、侧边栏抽屉等)时候,为了美观会加上一些过渡效果,比如宽度、高度的过渡等。

以高度为例,一般情况下,如果我们提前知道内容的固定高H,那么可以设置高度从0到H来控制显示隐藏,如下:

js 复制代码
 <template>
     <button @click="isShow = !isShow"></button>
     ... 假设固高度 300px
    <div class="content" :style="{ height: isShow ? '300px' : 0 }">
        ...
    </div>
  </tempalte>
  <script>
      ...
      const isShow = ref(false);
  </script>
  <style>
      .content{
         transition: height 0.3s;
      }
  </style>

但是,很多情况下,内容可能是动态生成的,导致高度未知,以上方案就会失效,即使设置 height 为auto也没效果。


解决思路

这种情况下就需要借助<Transtion>组件里的钩子周期函数来实现,并封装成组件复用。

1、首先先包装内置的 Transition 组件:

html 复制代码
<Transition
    @before-enter="onBeforeEnter"
    @enter="onEnter"
    @leave="onLeave"
    :css="false">
    <slot></slot>
</Transition>

css设置false原因:

(摘自vue官网) 在使用仅由 JavaScript 执行的动画时,最好是添加一个 :css="false" prop。这显式地向 Vue 表明可以跳过对 CSS 过渡的自动探测。除了性能稍好一些之外,还可以防止 CSS 规则意外地干扰过渡效果。

2、在 beforeEnter 周期函数里初始化样式。注意有些内容有设置padding,为了完全隐藏,还需要记录paddingToppaddingBottom

js 复制代码
  
const originStyle = ref(null);   
// 设置元素的 "enter-from" 状态
function onBeforeEnter(el) {
  //记录元素原来的属性
if (!originStyle.value) {
      const computedStyle = getComputedStyle(el)
      originStyle.value = {
          paddingTop: computedStyle.paddingTop,
          paddingBottom: computedStyle.paddingBottom,
          overflow: computedStyle.overflow
      }
  }

el.style.transition = "height 0.3s, padding 0.3s";
el.style.overflow = 'hidden';
el.style.height = '0';
el.style.paddingTop = '0'
el.style.paddingBottom = '0'
}
 

3、onEnter周期函数里通过scrollHeight获取到内容高度

js 复制代码
function onEnter(el, done) {
    // 使用 RAF 保证动画帧同步
    requestAnimationFrame(() => {
        el.style.height = `${el.scrollHeight}px`;
        el.style.paddingTop = originStyle.value.paddingTop;
        el.style.paddingBottom = originStyle.value.paddingBottom;
        // 监听过渡结束事件
        el.addEventListener('transitionend', done, { once: true })
    })
}

4、onLeave周期函数 height、padding重置为0

js 复制代码
function onLeave(el, done) {
    // 添加延迟保证过渡生效
    requestAnimationFrame(() => {
        el.style.height = '0';
        el.style.paddingTop = '0';
        el.style.paddingBottom = '0';
        el.addEventListener('transitionend', done, { once: true })
    })
}    

测试调用

封装完成后,就可以在任意地方使用,如下:

js 复制代码
 <template>
     <button @click="isShow = !isShow"></button>
     ... 
     <MyTransition>
        <div v-show="isShow" class="content">  //未知高度
        ...
        </div>
     </MyTransition>
  </tempalte>
  <script>
      import MyTransition from './MyTransition';
      
      const isShow = ref(false);
      
  </script>

至此,我们就解决了上述问题,完美实现任意内容的显示/隐藏过渡效果。

感谢阅读。

相关推荐
vipbic3 小时前
别再把“做个H5”挂嘴边了:这个词,官方压根就没有定义过
前端
ZC跨境爬虫4 小时前
跟着 MDN 学CSS day_39:(Flexbox 弹性盒子核心机制)
前端·css·ui·html·tensorflow
小陈同学呦4 小时前
前端如何处理订单状态导航的数据竞态问题
前端·javascript
喵个咪5 小时前
GoWind Toolkit 前端代码生成|Vue3(ElementPlus/Vben)、React(AntDesign)全自动一键生成教程
前端·vue.js·react.js
摆烂大大王6 小时前
玩转 OpenClaw:用 TaskFlow + Heartbeat 打造自动化工作流
前端·人工智能·自动化
zhangxingchao6 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
梦想的颜色7 小时前
TypeScript 完全指南(上):从零开始掌握类型系统
前端·typescript
之歆7 小时前
Day01_ES6+ 专业指南:从基础到实战的现代JavaScript开发(下)
前端·javascript·es6
lichenyang4537 小时前
鸿蒙 MVVM 实战:从 Demo 到工程化,聊聊登录、状态管理与埋点系统设计
前端
IT_陈寒7 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端