更新记录

1.0.0(2026-08-31) 下载此版本

  • 核心手势引擎:发布首个正式版本,内置丝滑物理拖拽手势与 0.05 黄金跟手旋转阻尼。
  • 多维空间堆叠:支持配置垂直偏移步长 (offsetY)、水平偏移步长 (offsetX)、缩放步长 (scaleStep)、错落旋转步长 (rotateStep) 以及透明度渐隐衰减 (fadeStep)。
  • 灵活模式支持:支持无限循环翻页 (loop: true) 与单次滑完判定 (loop: false)。
  • 交互防护机制:退场飞出卡片与底层未激活卡片全面隔离事件流 (pointer-events: none),杜绝误触与穿透形变。
  • 丰富插槽透传:支持通过作用域插槽获取 itemdragRatiodragDirection 等实时拖拽动力学数据。
  • 完整 API 与实例方法:支持通过 ref 实例方法进行编程式切牌 (swipe) 与状态归位 (reset)。

平台兼容性

uni-app(3.8.3)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟

jon-stack 卡片堆叠滑动组件

通用高性能卡片堆叠滑动容器,内置丝滑手势拖动与物理联动动画,支持图片背景或纯色背景作为卡片底图,并在上层提供完全自定义内容插槽,可配置任意层叠布局,开箱即用。

适用于「探探式」滑动卡片、商品推荐、图片浏览、短视频推荐等场景。


特性

  • 🎯 丝滑手势引擎:内置物理拖拽手势与 0.05 黄金跟手旋转阻尼,拖动、判定、飞出一气呵成
  • 🗂 多维空间堆叠:独立配置垂直偏移 offsetY、水平偏移 offsetX、缩放 scaleStep、错落旋转 rotateStep、透明度渐隐 fadeStep,任意排列组合
  • 🔁 双模式切换loop: true 无限循环翻页 / loop: false 单次滑完并触发 end 事件
  • 🛡 交互隔离防护:退场飞出卡片与底层未激活卡片全面隔离事件流(pointer-events: none),杜绝误触与穿透形变
  • 🎨 背景与插槽叠加:图片背景(imageKey)或纯色背景(bgKey)作为卡片底图,上层始终渲染完全自定义内容插槽;二者可同时使用,也可以都不设置背景
  • 📦 作用域插槽透传:实时获取 itemindexisCurrentdragRatiodragDirection 等拖拽动力学数据
  • 🎛 完整 API:ref 实例方法编程式切牌 swipe()、状态归位 reset()

安装

在 HBuilderX 中,将本插件目录(uni_modules/jon-stack)拷贝到项目的 uni_modules 目录下即可。

组件符合 easycom 规范,无需手动 import 与注册,页面中直接使用 <jon-stack /> 标签。


快速上手

<template>
    <view class="page">
        <jon-stack ref="stackRef" :list="cardList" @change="onChange" @swipe="onSwipe"
            @cardClick="onCardClick" @end="onEnd">
            <template #default="{ item }">
                <view class="card-body">
                    <text class="card-text">{{ item.title }}</text>
                </view>
            </template>
        </jon-stack>

        <view class="actions">
            <button @click="swipe('left')">左滑</button>
            <button @click="reset">重置</button>
            <button @click="swipe('right')">右滑</button>
        </view>
    </view>
</template>

<script setup>
    import { ref } from 'vue'

    const stackRef = ref(null)

    const cardList = ref([
        { id: 1, title: '卡片 1', image: '/static/1.jpg' },
        { id: 2, title: '卡片 2', bgColor: '#66ccff' },
        { id: 3, title: '卡片 3' }
    ])

    const onChange = (e) => {
        console.log('当前项:', e.current, e.item)
    }

    const onSwipe = (e) => {
        console.log('划出方向:', e.direction, '卡片:', e.item)
    }

    const onCardClick = (e) => {
        console.log('点击卡片:', e.item)
    }

    const onEnd = (e) => {
        console.log('全部滑完,共', e.total, '张')
    }

    const swipe = (dir) => stackRef.value?.swipe(dir)
    const reset = () => stackRef.value?.reset()
</script>

<style scoped>
    .page {
        padding: 60rpx 32rpx;
    }

    .card-body {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .card-text {
        font-size: 38rpx;
        font-weight: bold;
        color: #ffffff;
        text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.4);
    }

    .actions {
        display: flex;
        gap: 16rpx;
        margin-top: 80rpx;
    }
</style>

API

Props 属性

属性 类型 默认值 必填 说明
list Array [] 数据源列表,数组项为卡片数据对象
width String '680rpx' 卡片宽度,支持任意 CSS 尺寸
height String '860rpx' 卡片高度,支持任意 CSS 尺寸
borderRadius String '32rpx' 卡片圆角
shadow String '0 16rpx 48rpx rgba(0, 0, 0, 0.12)' 卡片阴影
imageKey String 'image' 数据项中图片字段的属性名(用于读取卡片底图)
imageMode String 'aspectFill' 图片裁剪缩放模式,同 uni-app image 组件 mode
bgKey String 'bgColor' 数据项中背景色字段的属性名
visibleCount Number 3 可见层数,取值范围 1 ~ 5
offsetY Number 36 垂直偏移步长(rpx),正数向下、负数向上、0 不位移
offsetX Number 0 水平偏移步长(rpx),正数向右、负数向左、0 不位移
scaleStep Number 0.05 缩放步长,正数逐层缩小、负数逐层放大、0 等大
rotateStep Number 0 层叠错落旋转步长(deg),正数顺时针、负数逆时针
transformOrigin String 'center center' 缩放 / 旋转基准中心点,如 'center bottom'
fadeStep Number 0 层级透明度衰减步长,取值 0 ~ 1,越大后层越透明
duration Number 300 动画过渡耗时(ms)
timingFunction String 'cubic-bezier(0.25, 1, 0.5, 1)' 缓动动画曲线,可传任意 CSS 过渡函数
threshold Number 80 判定滑出卡片的物理像素距离(px)
loop Boolean true 是否无限循环滑动,false 时滑完触发 end 事件
disabled Boolean false 是否禁用滑动交互与点击(true 时锁定卡片)

Events 事件

事件名 触发时机 回调参数
change 当前卡片切换(含编程式 swipe() { current, item, direction } — 当前项索引、当前项数据、划出方向 'left' / 'right'
swipe 卡片划出(手势或编程式) { direction, item, index } — 划出方向、划出卡片数据、在原始列表中的索引
cardClick 点击当前卡片(位移小于 5px 视为点击) { item, index } — 卡片数据、在原始列表中的索引
end 非循环模式下全部卡片滑完 { total } — 卡片总数

Slots 插槽

默认作用域插槽,用于完全自定义卡片内容,插槽容器铺满整张卡片:

插槽参数 类型 说明
item Object 当前卡片数据
index Number 当前卡片数据在原始列表中的索引(取模回绕后的值)
isCurrent Boolean 是否为当前最上层卡片
isNext Boolean 是否为下一张即将切换的卡片
absoluteIndex Number 卡片在渲染队列中的绝对索引(内部用)
dragRatio Number 当前卡片拖拽进度 0 ~ 1(仅当前卡片实时更新)
dragDirection String 当前拖拽方向:'left' / 'right' / 'none'

实例方法

通过 ref 获取组件实例后调用:

方法 参数 说明
swipe(direction) direction?: 'left' \| 'right',默认 'right' 编程式触发卡片划出动画,会同步触发 swipe / change / end 事件
reset() 重置卡片索引与拖拽位移状态,回到第一张卡片

数据项字段与插槽叠加

组件通过 imageKeybgKey 从数据项读取底图与背景色,并在其上层渲染默认插槽内容。图片/背景色作为卡片底层背景,插槽作为上层内容,二者可同时存在、互不冲突:

// 图片背景 + 插槽文字
{ id: 1, title: '图片卡', image: 'https://xxx/1.jpg' }

// 纯色背景 + 插槽内容
{ id: 2, title: '纯色卡', bgColor: '#66ccff' }

// 不设置背景:透明卡片,仅使用自定义插槽
{ id: 3, title: '自定义卡' }

常见布局推荐参数

组件本身不内置任何预设布局,以下参数组合来自示例工程的实践,可直接复用:

布局效果 参数组合
经典底部堆叠 offsetY=36, offsetX=0, rotateStep=0
顶部展开 offsetY=-36, offsetX=0, rotateStep=0
右侧展开 offsetY=0, offsetX=36, rotateStep=0
斜向错落 offsetY=24, offsetX=24, rotateStep=3
居中重叠 offsetY=0, offsetX=0, rotateStep=0
扇形旋转 offsetY=0, offsetX=0, rotateStep=6

注意事项

  1. 版本要求:本组件源码使用 Vue3 <script setup> 语法,推荐用于 uni-app Vue3 项目;HBuilderX 需 3.1.0+(与 package.jsonengines.HBuilderX 一致)。如需在 Vue2 中使用,需自行转换为 Options API 写法。
  2. 容器尺寸:组件默认 margin: 0 auto 居中,尺寸由 width / height 控制,请为容器预留足够空间。
  3. 触摸冲突:组件内部已设置 touch-action: none,若外层页面需要纵向滚动,请将组件放置在独立容器中,并合理控制卡片拖拽方向。
  4. 数据更新:动态增删 list 数据时,建议在变更后调用 reset() 使卡片状态归位。
  5. 图片字段:若数据项中无图片字段(或图片为空),组件自动跳过图片渲染,不影响插槽内容。
  6. 点击事件:需要区分「轻触点击」与「拖拽滑动」时,建议监听 cardClick 事件;插槽内自定义元素仍可正常绑定点击事件,但需注意与拖拽手势的冲突。

更新日志

详见 changelog.md

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。