序言
在上篇文章中,我们对折叠组件的开发做了前期铺垫,包括基本概念、思路和准备工作。现在,我们将深入其具体实现啦 !不过,开发过程中会有一些 “小坑” 。本文会详细阐述如何运用 Vue 特性将折叠组件的设计转化为代码,同时分享可能遇到的潜在问题及解决办法,让我们开启精彩的开发之旅 !
Transition
首先介绍一下vue的内置组件Transition
:它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:
- 由
v-if
所触发的切换 - 由
v-show
所触发的切换 - 由特殊元素
<component>
切换的动态组件 - 改变特殊的
key
属性
它有6个CSS class应用于进入与离开过渡效果:
enter-from
:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。enter-active
:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。enter-to
:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是v-enter-from
被移除的同时),在过渡或动画完成之后移除。leave-from
:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。leave-active
:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。leave-to
:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是v-leave-from
被移除的同时),在过渡或动画完成之后移除。
除了CSS class也是对应的8个钩子函数的:
before-enter
: 对应enter-from
enter
: 对应enter-active
after-enter
: 对应enter-to
enter-cancelled
:当进入过渡在完成之前被取消时调用before-leave
: 对应leave-from
leave
: 对应leave-active
after-leave
: 对应leave-to
leave-cancelled
:当进入过渡在结束之前被取消时调用 仅在 v-show 过渡中可用
BeforeEnter
过渡效果的本质是 CSS 属性值从一个数值变化到另一个数值。折叠组件原本可通过改变 DOM 元素的高度来实现,但实际开发中,DOM 元素高度往往需要自适应,难以提前确定准确高度,因此改变高度的方案不太可行。不过,我们可以通过改变元素的最大高度来实现。
const listeners = {
beforeEnter: (el: RendererElement) => {
if (!el.dataset) el.dataset = {}
const style = getComputedStyle(el as Element)
const { height, maxHeight, paddingTop, paddingBottom } = style
if (parseInt(height, 10)) el.dataset.height = height
if (parseInt(maxHeight, 10)) el.dataset.maxHeight = maxHeight
el.dataset.paddingTop = paddingTop
el.dataset.paddingBottom = paddingBottom
el.style.maxHeight = 0
el.style.paddingTop = 0
el.style.paddingBottom = 0
},
beforeEnter
函数在元素进入过渡动画前执行预处理操作。它接收一个元素el
,将元素设置为折叠状态,并记录其原始样式,为后续动画做好准备📋。
Enter
const getMaxHeight = (el: RendererElement) => {
let result = null
const { maxHeight, height } = el.dataset
if (maxHeight && height) {
result = parseInt(maxHeight, 10) < parseInt(height, 10) ? maxHeight : height
} else if (height) {
result = height
} else if (el.scrollHeight !== 0) {
result = el.scrollHeight + parseInt(el.dataset.paddingTop, 10) + parseInt(el.dataset.paddingBottom, 10) + 'px'
result = parseInt(maxHeight, 10) < parseInt(result, 10) ? maxHeight : result
}
return result
}
const listeners = {
enter: (el: RendererElement) => {
requestAnimationFrame(() => {
el.dataset.overflow = el.style.overflow
el.style.maxHeight = getMaxHeight(el)
el.style.paddingTop = el.dataset.paddingTop
el.style.paddingBottom = el.dataset.paddingBottom
el.style.overflow = 'hidden'
})
},
}
首先,getMaxHeight
的函数,它接收一个 RendererElement
类型的元素 el
作为参数。该函数的主要目的是计算并返回元素合适的高度值。首先,它尝试从元素的 dataset
中获取 maxHeight
和 height
属性。若这两个属性都存在,就比较它们转换为整数后的大小,将较小的值赋给结果变量 result
;若只有 height
存在,就直接将其赋值给 result
。若元素的 scrollHeight
不为 0,会将 scrollHeight
与 dataset
中的 paddingTop
和 paddingBottom
值相加,再转换为带 px
单位的字符串作为新的高度值,接着再与 maxHeight
比较大小,将较小的值赋给 result
。最后返回 result
。
listeners
对象中的enter
方法在元素进入过渡动画时执行。借助requestAnimationFrame
确保在浏览器下一帧渲染前执行后续操作,防止样式闪烁。该方法先将元素当前的overflow
样式值存入dataset
,再调用getMaxHeight
函数获取元素展开时的最大高度并设置到maxHeight
样式属性上,接着从dataset
恢复元素的顶部和底部内边距样式,最后将overflow
样式设为hidden
,确保展开过程中内容不会溢出,实现平滑的展开动画🎈。
AfterEnter、EnterCancelled
const reset = (el: RendererElement) => {
const { maxHeight, height, overflow, paddingTop, paddingBottom } = el.dataset
el.style.height = height;
el.style.maxHeight = maxHeight;
el.style.overflow = overflow;
el.style.paddingTop = paddingTop;
el.style.paddingBottom = paddingBottom;
}
const listeners = {
afterEnter: (el: RendererElement) => {
el.style.overflow = el.dataset.overflow
},
enterCancelled: (el: RendererElement) => {
reset(el)
},
}
reset
函数负责将元素样式恢复到原始状态。它从元素的dataset
中获取之前存储的高度、最大高度、溢出处理方式、顶部和底部内边距等样式信息,并重新应用到元素的style
属性上。
afterEnter
方法在元素展开动画结束后执行,将元素的overflow
样式恢复为dataset
中存储的值,保证元素最终的溢出显示状态符合初始设置。
enterCancelled
方法在元素展开动画被取消时执行,调用reset
函数,将元素样式重置为动画开始前的状态,维持界面的一致性🖥️。
BeforeLeave
const listeners = {
beforeLeave: (el: RendererElement) => {
if (!el.dataset) el.dataset = {}
el.dataset.paddingTop = el.style.paddingTop
el.dataset.paddingBottom = el.style.paddingBottom
el.dataset.overflow = el.style.overflow
el.style.maxHeight = getMaxHeight(el)
el.style.overflow = 'hidden'
},
}
listeners
对象中的beforeLeave
方法在元素开始离开过渡动画前进行准备工作。它首先检查元素是否有dataset
属性,若没有则进行初始化。接着,将元素当前的顶部内边距、底部内边距和溢出处理方式存储到dataset
中,以便后续恢复。然后,调用getMaxHeight
函数获取元素合适的最大高度并设置到maxHeight
样式属性上,同时将overflow
样式设为hidden
,避免内容溢出,为离开过渡动画做好样式准备📦。
Leave、AfterLeave、LeaveCancelled
const listeners = {
beforeLeave: (el: RendererElement) => {
if (!el.dataset) el.dataset = {}
el.dataset.paddingTop = el.style.paddingTop
el.dataset.paddingBottom = el.style.paddingBottom
el.dataset.overflow = el.style.overflow
el.style.maxHeight = getMaxHeight(el)
el.style.overflow = 'hidden'
},
leave: (el: RendererElement) => {
if (el.scrollHeight !== 0) {
el.style.maxHeight = 0
el.style.paddingTop = 0
el.style.paddingBottom = 0
}
},
afterLeave: (el: RendererElement) => {
reset(el)
},
leaveCancelled: (el: RendererElement) => {
reset(el)
}
}
beforeLeave
方法在离开过渡动画开始前,检查元素是否有dataset
属性,若没有则初始化。然后将元素当前的顶部和底部内边距、溢出处理方式存储在dataset
中,调用getMaxHeight
函数获取合适的最大高度并设置给元素,同时将溢出方式设为hidden
,为后续离开动画做准备。
leave
方法在离开过渡动画开始时执行。若元素有内容(scrollHeight
不为 0),将元素的最大高度、顶部和底部内边距都设为 0,使元素开始收缩,实现折叠效果。
afterLeave
方法在离开过渡动画结束后执行,调用reset
函数将元素样式恢复到初始状态,保证动画结束后元素样式符合预期。
leaveCancelled
方法在离开过渡动画被取消时执行,同样调用reset
函数,将元素样式重置,避免因动画取消而出现样式异常🧐。
这些钩子函数相互协作,实现了折叠组件离开动画过程中的样式管理和状态恢复,让用户在操作折叠组件时获得流畅的视觉体验。
展示
🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。
诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ !
👉点我
感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!
Nova UI
组件库:https://github.com/gmingchen/nova-ui- 基于 Vue3 + Element-plus 管理后台基础功能框架
- 预览:https://admin.gumingchen.icu
- 基于 Vue3 + Element-plus + websocket 即时聊天系统
- 基于 node 开发的后端服务:https://github.com/gmingchen/node-server