更新记录
1.0.0(2025-10-19) 下载此版本
v1.0.0 (2024-10-19)
平台兼容性
uni-app(4.81)
Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
---|---|---|---|---|---|---|---|---|
× | √ | - | - | - | - | - | - | - |
微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
---|---|---|---|---|---|---|---|---|---|---|
- | - | - | - | - | - | - | - | - | - | - |
可拖拽浮动面板组件 (DraggablePanel)
项目简介
一个基于 uni-app + Vue3 开发的高性能可拖拽浮动面板组件,支持触摸拖拽、自动吸附、内容滚动等丰富功能。该组件特别适用于移动端应用中需要展示详情信息的场景,如地图应用的位置详情、电商应用的商品详情、美食应用的店铺信息等。
技术栈
- 框架: uni-app 3.0
- 语言: Vue 3 + JavaScript
- 样式: SCSS
- 构建工具: Vite 5.2.8
- 支持平台: H5、微信小程序、支付宝小程序、百度小程序等全平台
核心特性
🎯 主要功能
-
触摸拖拽交互
- 流畅的原生触摸事件处理
- 支持上拉展开、下拉收起
- 实时位置跟随,无延迟感
-
自动吸附
- 智能边界吸附
- 可配置吸附阈值
- 平滑动画过渡
-
内容滚动
- 内部内容可独立滚动
- 支持 uni-app 增强滚动
- 滚动条显示控制
-
动态高度
- 自动计算容器高度
- 适配不同屏幕尺寸
- 支持导航栏高度兼容
-
编程式控制
- 提供展开/收起/切换方法
- 支持设置具体位置
- 丰富的事件回调
🎨 视觉特性
- Material Design 风格阴影效果
- 可自定义圆角和背景色
- 优雅的拖拽指示器
- 平滑的动画过渡效果
项目结构
my-FloatingPanel/
├── src/
│ ├── pages/
│ │ └── index/
│ │ ├── index.vue # 示例页面(美食详情)
│ │ ├── index2.vue # 备用页面
│ │ └── DraggablePanel/
│ │ └── index.vue # 可拖拽面板组件
│ ├── App.vue # 应用入口
│ ├── main.js # 主入口文件
│ ├── manifest.json # 应用配置
│ └── pages.json # 页面配置
├── package.json # 项目依赖
└── vite.config.js # Vite配置
组件使用
基础用法
<template>
<DraggablePanel
ref="draggablePanel"
:initialTop="'500rpx'"
:minTranslateY="-500"
:maxTranslateY="0"
:showHandle="true"
:scrollable="true"
@change="onPanelChange"
@expand="onPanelExpand"
@collapse="onPanelCollapse"
>
<!-- 你的内容 -->
<view class="content">
<!-- 内容区域 -->
</view>
</DraggablePanel>
</template>
<script setup>
import DraggablePanel from './DraggablePanel/index.vue'
import { ref } from 'vue'
const draggablePanel = ref(null)
// 面板位置变化
const onPanelChange = (data) => {
console.log('位置变化', data.translateY, '进度', data.progress)
}
// 面板展开
const onPanelExpand = () => {
console.log('面板已展开')
}
// 面板收起
const onPanelCollapse = () => {
console.log('面板已收起')
}
</script>
编程式控制
// 展开面板
draggablePanel.value?.expand()
// 收起面板
draggablePanel.value?.collapse()
// 切换状态
draggablePanel.value?.toggle()
// 设置具体位置
draggablePanel.value?.setTranslateY(-250)
// 获取当前状态
const isExpanded = draggablePanel.value?.isExpanded
const currentPosition = draggablePanel.value?.translateY
API 文档
Props 属性
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
位置控制 | |||
initialTranslateY | 初始Y轴偏移量 | Number | 0 |
minTranslateY | 最小偏移量(最大上拉距离) | Number | -500 |
maxTranslateY | 最大偏移量(最大下拉距离) | Number | 0 |
initialTop | 初始顶部位置 | String | '500rpx' |
手柄配置 | |||
showHandle | 是否显示拖拽手柄 | Boolean | true |
handleColor | 拖拽手柄颜色 | String | '#d9d9d9' |
滚动配置 | |||
scrollable | 是否启用内容滚动 | Boolean | true |
enhanced | 是否启用增强滚动 | Boolean | true |
showScrollbar | 是否显示滚动条 | Boolean | false |
吸附动画 | |||
autoSnap | 是否启用自动吸附 | Boolean | true |
snapThreshold | 自动吸附阈值(0-1) | Number | 0.5 |
animationDuration | 动画持续时间(ms) | Number | 300 |
外观配置 | |||
borderRadius | 面板圆角设置 | String | '20rpx 20rpx 0rpx 0rpx' |
backgroundColor | 面板背景颜色 | String | '#ffffff' |
enableShadow | 是否启用阴影效果 | Boolean | true |
高度配置 | |||
dynamicHeight | 是否启用动态高度 | Boolean | true |
Events 事件
事件名 | 说明 | 回调参数 |
---|---|---|
change | 面板位置变化时触发 | { translateY, progress } |
dragstart | 开始拖拽时触发 | { translateY, pageY } |
dragend | 结束拖拽时触发 | { translateY, oldTranslateY, isExpanded } |
expand | 面板展开时触发 | - |
collapse | 面板收起时触发 | - |
scroll | 内容滚动时触发 | { type, scrollTop, deltaX, deltaY } |
Methods 方法
方法名 | 说明 | 参数 |
---|---|---|
expand() | 展开面板 | - |
collapse() | 收起面板 | - |
toggle() | 切换面板状态 | - |
setTranslateY(value) | 设置面板位置 | value: Number |
Expose 暴露属性
属性名 | 说明 | 类型 |
---|---|---|
translateY | 当前Y轴偏移量 | Ref |
isExpanded | 是否已展开 | ComputedRef |
使用场景示例
1. 美食详情页(当前示例)
项目中的 index.vue
展示了一个美食详情页的实现:
- 顶部区域: 美食主图展示
- 浮动面板: 包含美食名称、标签、评分、描述和相关餐厅列表
- 交互特性: 上拉查看更多内容,下拉回到概览状态
2. 地图位置详情
<DraggablePanel
:initialTop="'600rpx'"
:minTranslateY="-400"
:maxTranslateY="0"
>
<view class="location-info">
<view class="location-name">{{ location.name }}</view>
<view class="location-address">{{ location.address }}</view>
<view class="location-actions">
<button>导航</button>
<button>分享</button>
</view>
</view>
</DraggablePanel>
3. 商品快速预览
<DraggablePanel
:initialTop="'400rpx'"
:minTranslateY="-600"
:snapThreshold="0.3"
>
<view class="product-preview">
<image :src="product.image" />
<view class="product-info">
<text class="price">¥{{ product.price }}</text>
<button @click="addToCart">加入购物车</button>
</view>
</view>
</DraggablePanel>
性能优化
已实现的优化
-
使用 transform 而非 top/bottom
- 利用 GPU 加速,避免重排重绘
- 确保 60fps 的流畅动画
-
拖拽时禁用过渡动画
- 消除拖拽延迟感
- 提高响应速度
-
事件防抖与节流
- 优化滚动事件处理
- 减少不必要的计算
-
动态高度计算缓存
- 使用 computed 缓存计算结果
- 避免重复计算
建议的最佳实践
-
合理设置初始位置
- 根据内容重要性设置 initialTop
- 避免初始位置过高或过低
-
适当的吸附阈值
- 一般设置为 0.3-0.7
- 根据用户习惯调整
-
内容懒加载
- 展开时再加载详细内容
- 减少初始渲染压力
开发指南
环境准备
# 安装依赖
npm install
# 运行到 H5
npm run dev:h5
# 运行到微信小程序
npm run dev:mp-weixin
# 运行到支付宝小程序
npm run dev:mp-alipay
代码规范
- 组件命名: 使用 PascalCase,如
DraggablePanel
- 事件命名: 使用 kebab-case,如
@drag-start
- 属性命名: 使用 camelCase,如
minTranslateY
- 样式命名: 使用 kebab-case + BEM,如
.drag-handle__bar
调试技巧
-
查看高度计算日志
// 组件内已包含调试日志 console.log('高度计算:', { screenHeight, availableHeight, finalHeight, translateY })
-
监听所有事件
<DraggablePanel @change="log('change', $event)" @dragstart="log('dragstart', $event)" @dragend="log('dragend', $event)" @expand="log('expand')" @collapse="log('collapse')" @scroll="log('scroll', $event)" />
兼容性
平台 | 支持版本 | 测试状态 |
---|---|---|
H5 | ✅ 全版本 | 已测试 |
微信小程序 | ✅ 基础库 2.0+ | 已测试 |
支付宝小程序 | ✅ 全版本 | 待测试 |
百度小程序 | ✅ 全版本 | 待测试 |
QQ小程序 | ✅ 全版本 | 待测试 |
字节小程序 | ✅ 全版本 | 待测试 |
快应用 | ✅ 全版本 | 待测试 |
App | ✅ 全版本 | 待测试 |
常见问题
Q: 面板拖拽不流畅怎么办?
A: 检查以下几点:
- 确保没有在 touchmove 事件中进行复杂计算
- 检查是否正确设置了
is-dragging
类 - 避免在拖拽时触发其他动画
Q: 内容滚动与拖拽冲突怎么处理?
A: 组件已内置处理逻辑:
- 内容滚动到顶部时才能下拉
- 通过
isScrollAtTop
状态控制
Q: 如何自定义过渡动画?
A: 修改组件样式中的 transition 属性:
.draggable-panel {
transition: transform 0.5s ease-in-out; // 自定义时长和缓动
}
Q: 支持横向拖拽吗?
A: 当前版本仅支持纵向拖拽。如需横向拖拽,可修改:
handleTouchMove
中的 deltaX 计算transform
使用 translateX
更新日志
v1.0.0 (2024-10-19)
- 🎉 初始版本发布
- ✨ 实现基础拖拽功能
- ✨ 添加自动吸附特性
- ✨ 支持内容滚动
- ✨ 提供编程式控制接口
- 📝 完善组件文档
贡献指南
欢迎提交 Issue 和 Pull Request!
提交 Issue
- 使用清晰的标题描述问题
- 提供复现步骤和环境信息
- 附上相关代码和截图
提交 PR
- Fork 项目并创建特性分支
- 编写清晰的提交信息
- 确保代码符合项目规范
- 添加必要的注释和文档
许可证
MIT License
联系方式
- 作者邮箱: [***@qq.com]