Quasar QSlideItem 组件完全指南:从左右滑动操作到源码级原理剖析
【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar
QSlideItem 是 Quasar Framework 中用于实现"滑动即操作"交互的列表项组件,本质上是在 QItem 基础上扩展了left与right(乃至top、bottom)插槽,让用户通过鼠标拖拽或触屏手指滑动即可触发指定动作。本文以官方文档 slide-item.md 为主体,结合 QSlideItem.js 源码与 5 个官方示例,完整覆盖其 API、四向滑动、自定义颜色、滑动过程动态定制、单侧布局与无障碍要求,帮助你直接上手并理解其底层手势判定逻辑。
组件定位:QItem 之上的滑动动作层
官方文档开篇即给出定义:QSlideItem 本质上就是一个 QItem,只是额外增加了两个插槽(left和right),允许用户将条目拖向某一侧来执行特定动作。换言之,它继承了 QItem 的一切布局与语义能力,同时叠加了一层"滑动揭示操作区"的交互。
与之相关的组件还包括 QExpansionItem(展开式列表项)与 QMenu(菜单,可在无障碍场景中承载等价操作),它们共同构成了 Quasar 列表交互的完整工具箱。
从源码看,组件在 QSlideItem.js 中通过createComponent注册,props 只声明了四个方向颜色与dark(继承自useDarkProps),事件则固定为action、top、right、bottom、left五个——这种"少 props、多插槽、事件驱动"的设计,正是滑动操作组件的典型架构。
API 速览:Props、Slots、Events 与 Methods
组件完整的类型与参数定义见官方 JSON 描述文件 QSlideItem.json,核心契约如下:
Props(属性)
| 属性名 | 类型 | 说明 |
|---|---|---|
left-color | String | 左侧滑动区背景色,取值来自 Quasar Color Palette(如red、primary、amber) |
right-color | String | 右侧滑动区背景色 |
top-color | String | 顶部滑动区背景色 |
bottom-color | String | 底部滑动区背景色 |
dark | Boolean | 是否启用暗色模式样式 |
四个颜色属性均继承自统一的color基础定义(category 为 style),意味着你可以传入任何 Quasar 调色板颜色名,组件会拼接出bg-<color>类。
Slots(插槽)
| 插槽名 | 说明 |
|---|---|
default | 条目主体内容所在位置,官方建议使用 QItemSection 组织 |
left | 向左滑动时揭示的左侧内容 |
right | 向右滑动时揭示的右侧内容 |
top | 向上滑动时揭示的顶部内容 |
bottom | 向下滑动时揭示的底部内容 |
注意:只有定义了对应插槽的方向才可滑动;未定义插槽的方向,源码中会在手势阶段直接将位移归零(见下文原理分析)。
Events(事件)
| 事件名 | 触发时机 | 回调参数 |
|---|---|---|
left/right/top/bottom | 用户完成向该方向的滑动(滑动距离达标后) | { reset }:reset为函数,调用后将组件复位到未滑动状态 |
slide | 滑动过程中持续触发 | { side, ratio, isReset }:side为'left' | 'right' | 'top' | 'bottom';ratio为 0~1 的进度比例;isReset为布尔值,表示比例是否已复位 |
action | 用户完成向任意一侧的滑动 | { side, reset }:side指明生效方向,reset为复位函数 |
Methods(方法)
| 方法名 | 说明 |
|---|---|
reset | 将组件复位到初始(未滑动)状态。源码中通过Object.assign(proxy, { reset })暴露为公开方法,可在模板中用 ref 调用 |
基础用法:横向左右滑动
最基本的场景是左右两个方向各揭示一个操作区。官方示例 Basic.vue 展示了三种内容组合——纯图标、纯文本、图标加文本:
<template> <div class="q-pa-md" style="max-width: 350px"> <q-list bordered separator> <q-slide-item @left="onLeft" @right="onRight"> <template #left> <q-icon name="done" /> </template> <template #right> <q-icon name="alarm" /> </template> <q-item> <q-item-section avatar> <q-avatar color="primary" text-color="white" icon="bluetooth" /> </q-item-section> <q-item-section>Icons only</q-item-section> </q-item> </q-slide-item> </q-list> </div> </template> <script setup> import { useQuasar } from 'quasar' import { onBeforeUnmount } from 'vue' const $q = useQuasar() let timer function finalize(reset) { timer = setTimeout(() => { reset() }, 1000) } onBeforeUnmount(() => { clearTimeout(timer) }) function onLeft({ reset }) { $q.notify('Left action triggered. Resetting in 1 second.') finalize(reset) } function onRight({ reset }) { $q.notify('Right action triggered. Resetting in 1 second.') finalize(reset) } </script>几个关键实战要点:
- 事件回调解构
reset:四个方向事件(left/right/top/bottom)和action事件都会传入reset函数。滑动触发动作后组件会停留在"揭示"状态,必须调用reset()才能归位。示例中用setTimeout延迟 1 秒自动复位,并用onBeforeUnmount清理定时器,避免组件卸载后仍执行回调。 - 内容中的图片需禁用原生拖拽:文档特别提示,若条目内容包含图片,应为其添加
draggable="false",否则浏览器原生图片拖拽行为会干扰滑动手势。官方所有示例的<img>都遵循了这一约定。 - 滑动区内容推荐使用
row items-center包装:从示例可见,左侧/右侧插槽内容通常配合q-icon与文字,用 flex 布局保证垂直居中。
垂直滑动:top / bottom 方向
QSlideItem 不仅支持水平方向,还支持垂直方向的上下滑动。官方示例 Vertical.vue 展示了一个 150px 高条目的上下滑动:
<template> <div class="q-pa-md" style="max-width: 220px"> <q-list bordered separator> <q-slide-item @top="onTop" @bottom="onBottom"> <template #top> <q-icon name="link" /> </template> <template #bottom> <q-icon name="link_off" /> </template> <q-item style="height: 150px"> <q-item-section avatar> <q-avatar color="primary" text-color="white" icon="fingerprint" /> </q-item-section> <q-item-section>Slide vertically</q-item-section> </q-item> </q-slide-item> </q-list> </div> </template>与横向用法完全对称:定义top/bottom插槽并监听对应事件即可。从源码看,垂直与水平的判定是在手势开始时就确定轴向(pan.axis),一旦确定为 Y 轴,后续位移只按offset.y计算方向,不会中途切换轴向。
自定义颜色:为每个方向赋予语义色
通过left-color/right-color/top-color/bottom-color四个 props,可以为滑动操作区设置任意 Quasar 调色板颜色,用色彩传达动作语义(例如红色代表删除、绿色代表完成)。官方示例 CustomColors.vue:
<q-slide-item @left="onLeft" @right="onRight" left-color="red" right-color="purple" > <template #left> <div class="row items-center"> <q-icon left name="done" /> Left </div> </template> <template #right> <div class="row items-center"> Right content.. long <q-icon right name="alarm" /> </div> </template> <q-item> <q-item-section avatar> <q-icon color="primary" name="cell_wifi" /> </q-item-section> <q-item-section>Custom colors (red, purple)</q-item-section> </q-item> </q-slide-item>默认情况下四个方向的操作区背景色由 QSlideItem.sass 定义:left 为绿色、right 为橙色、top 为蓝色、bottom 为紫色,字体颜色统一为白色。传入颜色 props 后,源码会在对应方向容器的 class 上追加bg-<color>(见 QSlideItem.js),覆盖默认主题色。
滑动过程实时定制:slide 事件与 ratio
如果希望在滑动过程中动态改变样式(比如滑动越深、颜色越浓),需要监听slide事件。该事件在每次手势位移时触发,回调携带{ side, ratio, isReset }:
side:当前生效方向;ratio:已完成滑动比例,范围 0(未滑动)到 1(完全滑开),由源码Math.max(0, Math.min(1, (dist - 40) / pan.size[showing]))计算得出;isReset:为true表示比例已被复位(手指松开且未达标,或调用了 reset)。
官方示例 CustomizeSlide.vue 利用ratio动态计算左右操作区的颜色深浅:
<script setup> import { computed, ref } from 'vue' const slideRatio = ref({ left: 0, right: 0 }) const leftColor = computed(() => slideRatio.value.left >= 1 ? 'red-10' : 'red-' + (3 + Math.round(Math.min(3, slideRatio.value.left * 3))) ) const rightColor = computed(() => slideRatio.value.right >= 1 ? 'green-10' : 'green-' + (3 + Math.round(Math.min(3, slideRatio.value.right * 3))) ) function onSlide({ side, ratio, isReset }) { clearTimeout(timer) timer = setTimeout( () => { slideRatio.value[side] = ratio }, isReset ? 200 : void 0 ) } </script><q-slide-item :left-color="leftColor" :right-color="rightColor" @left="onLeft" @right="onRight" @slide="onSlide" > <template #left> Left </template> <template #right> Right content.. long </template> <q-item>...</q-item> </q-slide-item>这里leftColor是一个根据slideRatio.left在red-3到red-10之间渐变取值的 computed 属性,实现了"滑得越深颜色越重"的反馈效果;isReset为真时用 200ms 延迟把比例归零,保证复位过程也有平滑过渡。
单侧或无操作方向:OneSided 场景
并非每个条目都需要左右两个方向的操作。官方示例 OneSided.vue 展示了三种情况:只有左侧操作、只有右侧操作、完全没有操作:
<!-- 只定义 left 插槽:仅向左可滑动 --> <q-slide-item @left="onLeft" @right="onRight"> <template #left> <q-icon name="done" /> </template> <q-item>...Only left action...</q-item> </q-slide-item> <!-- 只定义 right 插槽:仅向右可滑动 --> <q-slide-item @left="onLeft" @right="onRight"> <template #right> <q-icon name="alarm" /> </template> <q-item>...Only right action...</q-item> </q-slide-item> <!-- 不定义任何滑动插槽:退化为普通条目 --> <q-slide-item @left="onLeft" @right="onRight"> <q-item>...No actions...</q-item> </q-slide-item>从源码 QSlideItem.js 可以确认这一行为:当手势方向对应的插槽未定义时(slots.left === void 0等),组件直接执行transform: translate(0,0)并 return,手势不会产生任何位移。同时渲染层会计算实际存在的方向列表dirs,仅当dirs.length === 0时完全跳过 TouchPan 手势指令的绑定(QSlideItem.js),此时组件从交互层面退化为一个普通列表项。
源码级原理剖析:TouchPan 与手势判定算法
理解了使用层面,再看 QSlideItem.js 的核心实现,可以更准确地预判组件行为:
1. 手势依赖 TouchPan 指令。组件引入 Quasar 的TouchPan指令(源码路径 TouchPan.js),并通过withDirectives以缓存方式绑定到内容节点上,修饰符固定为prevent、stop、mouse加上实际方向(QSlideItem.js)。mouse: true意味着桌面端鼠标拖拽同样可用,这正是"鼠标或手指皆可滑动"的底层来源。
2. 方向插槽元数据。顶部定义的slotsDef数组(QSlideItem.js)描述了每个方向的布局参数:
const slotsDef = [ ['left', 'center', 'start', 'width'], ['right', 'center', 'end', 'width'], ['top', 'start', 'center', 'height'], ['bottom', 'end', 'center', 'height'] ]其中第二、三项分别用于生成items-<align>与justify-<align>定位类,第四项(width/height)则是测量滑动区尺寸用的getBoundingClientRect()属性——这决定了滑动达标所需的位移基准。
3. 滑动进度算法。手势开始(evt.isFirst)时先测量各方向操作区尺寸并锁定轴向;位移过程中按pan.scale = clamp((dist - 40) / size, 0, 1)计算比例(QSlideItem.js)。40 是内置的起始阈值:位移不足 40px 时比例始终为 0,操作区保持隐藏,避免轻微抖动误触。
4. 触发与 230ms 延迟。手势结束(evt.isFinal)时,若pan.scale === 1(完全滑开),节点平移 100% 后延迟 230ms才依次emit方向事件与action事件(QSlideItem.js);若未达 1,则复位位移并触发slide事件且isReset: true。因此方向事件和action事件几乎同时触发,但都发生在用户松手之后的确认阶段。
5. RTL 支持。组件通过$q.lang.rtl判断语言方向(QSlideItem.js),在 RTL 语言下left与right的映射自动互换,保证滑动方向与阅读方向一致。
6. 过渡动画。内容层的位移动画由 QSlideItem.sass 的.q-slide-item__content控制:transition: transform .2s ease-in,并在手势过程中动态添加/移除no-transition类,实现"跟手实时位移、松手平滑归位"的体验;user-select: none则避免拖拽时选中文本。
无障碍:滑动必须只是"便捷方式"而非唯一入口
官方文档在 Accessibility 一节(标注 v2.25+ 起)给出了明确的警告:滑动手势仅支持指针操作——没有键盘交互,也没有任何辅助技术路径可以触发它。这意味着:
- 屏幕阅读器用户、纯键盘用户无法通过滑动触发任何绑定在滑动事件上的行为;
- 因此,任何通过 QSlideItem 滑动实现的动作(如删除、收藏、标记已读),都必须同时提供等价的替代入口——例如条目上的可见按钮,或一个包含相同命令的 QMenu;
- 滑动手势应始终被视为"便捷操作",而非唯一操作路径。
这一要求来自 Quasar 对 Web 可访问性的整体承诺,在实现滑动交互时务必作为硬性设计约束落实。
最佳实践小结
- 内容中的图片务必加
draggable="false",否则原生图片拖拽会干扰手势; - 事件回调里记得调用
reset(),否则条目会停留在滑动揭示状态;若用定时器延迟复位,务必在onBeforeUnmount中清理; - 只为需要操作的方向定义插槽,未定义方向会自动禁用,组件也会跳过手势指令绑定;
- 用颜色 props 表达动作语义,并结合
slide事件与ratio实现"滑动越深反馈越强"的动态效果; - 始终提供非手势的等价操作入口(可见按钮或菜单),满足键盘与屏幕阅读器用户;
- 想要程序化复位时,可通过模板 ref 调用组件暴露的
reset方法。
QSlideItem 的完整 API 细节可在 QSlideItem.json 中查阅,5 个官方示例源码分别位于 docs/src/examples/QSlideItem/ 目录下,可直接复制到项目中验证上述所有行为。
【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考