当前位置: 首页 > wzjs >正文

北京网站优化服务有限公司液压电机东莞网站建设

北京网站优化服务有限公司,液压电机东莞网站建设,丰台新乡网站建设,房地产网站模板 下载如何使用 UniApp 实现一个兼容 H5 和小程序的 九宫格拖拽排序组件&#xff0c;实现思路和关键步骤。 一、实现目标 支持拖动菜单项改变顺序拖拽过程实时预览移动位置拖拽松开后自动吸附回网格兼容 H5 和小程序平台 二、功能结构拆解以及完整代码 完整代码&#xff1a; <…

如何使用 UniApp 实现一个兼容 H5 和小程序的 九宫格拖拽排序组件,实现思路和关键步骤。

一、实现目标

  • 支持拖动菜单项改变顺序
  • 拖拽过程实时预览移动位置
  • 拖拽松开后自动吸附回网格
  • 兼容 H5 和小程序平台

二、功能结构拆解以及完整代码

完整代码:

<template><view class="container"><view class="menu-title">菜单列表</view><view class="grid-container"><viewclass="grid-item"v-for="(item, index) in menuList":key="index":class="{ 'active': currentIndex === index }":style="getPositionStyle(index)"@touchstart="handleTouchStart($event, index)"@touchmove.stop.prevent="handleTouchMove($event)"@touchend="handleTouchEnd"><view class="item-content"><view class="item-icon"><uni-icons :type="item.icon || 'star'" size="24"></uni-icons></view><view class="item-name">{{ item.name }}</view></view></view></view></view>
</template><script>
export default {name: 'MenuGrid',data() {return {// 菜单项列表menuList: [{ name: '首页', icon: 'home' },{ name: '消息', icon: 'chat' },{ name: '联系人', icon: 'contact' },{ name: '日历', icon: 'calendar' },{ name: '设置', icon: 'gear' },{ name: '相册', icon: 'image' },{ name: '文件', icon: 'folder' },{ name: '位置', icon: 'location' },{ name: '收藏', icon: 'star-filled' },{ name: '视频', icon: 'videocam' },{ name: '音乐', icon: 'sound' },{ name: '订单', icon: 'paperplane' }],// 网格配置columns: 4,     // 每行显示的列数itemSize: 80,   // 每个项目的大小 (单位px)itemGap: 15,    // 项目之间的间隔// 拖拽状态currentIndex: -1, // 当前拖拽的项目索引startX: 0,       // 触摸开始X坐标startY: 0,       // 触摸开始Y坐标moveOffsetX: 0,  // X轴移动的距离moveOffsetY: 0,  // Y轴移动的距离positions: [],   // 所有项目的位置isDragging: false // 是否正在拖拽}},mounted() {this.initPositions();},methods: {// 初始化所有项目的位置initPositions() {this.positions = [];const { itemSize, itemGap, columns } = this;this.menuList.forEach((_, index) => {const row = Math.floor(index / columns);const col = index % columns;// 计算项目位置this.positions.push({x: col * (itemSize + itemGap),y: row * (itemSize + itemGap),zIndex: 1});});},// 获取项目定位样式getPositionStyle(index) {if (!this.positions[index]) return '';const position = this.positions[index];const { itemSize } = this;return {transform: `translate3d(${position.x}px, ${position.y}px, 0)`,width: `${itemSize}px`,height: `${itemSize}px`,zIndex: position.zIndex || 1};},// 处理触摸开始handleTouchStart(event, index) {if (this.isDragging) return;const touch = event.touches[0];this.currentIndex = index;this.startX = touch.clientX;this.startY = touch.clientY;this.moveOffsetX = 0;this.moveOffsetY = 0;this.isDragging = true;// 提升当前项的层级this.positions[index].zIndex = 10;// 震动反馈uni.vibrateShort();},// 处理触摸移动handleTouchMove(event) {if (this.currentIndex === -1 || !this.isDragging) return;const touch = event.touches[0];// 计算移动距离const deltaX = touch.clientX - this.startX;const deltaY = touch.clientY - this.startY;this.moveOffsetX += deltaX;this.moveOffsetY += deltaY;// 更新拖拽项的位置this.positions[this.currentIndex].x += deltaX;this.positions[this.currentIndex].y += deltaY;// 更新开始位置,用于下一次移动计算this.startX = touch.clientX;this.startY = touch.clientY;// 检查是否需要交换位置this.checkForSwap();},// 处理触摸结束handleTouchEnd() {if (this.currentIndex === -1) return;// 重置拖拽项的层级if (this.positions[this.currentIndex]) {this.positions[this.currentIndex].zIndex = 1;}// 将所有项吸附到网格this.snapAllItemsToGrid();// 重置拖拽状态this.isDragging = false;this.currentIndex = -1;this.moveOffsetX = 0;this.moveOffsetY = 0;// 触发排序完成事件this.$emit('sort-complete', [...this.menuList]);},// 将所有项吸附到网格snapAllItemsToGrid() {const { itemSize, itemGap, columns } = this;this.menuList.forEach((_, index) => {const row = Math.floor(index / columns);const col = index % columns;this.positions[index] = {x: col * (itemSize + itemGap),y: row * (itemSize + itemGap),zIndex: 1};});},// 检查是否需要交换位置checkForSwap() {if (this.currentIndex === -1) return;const currentPos = this.positions[this.currentIndex];const { itemSize, itemGap } = this;let closestIndex = -1;let minDistance = Number.MAX_VALUE;// 找出与当前拖拽项距离最近的项this.positions.forEach((pos, index) => {if (index !== this.currentIndex) {// 计算中心点之间的距离const centerX1 = currentPos.x + itemSize / 2;const centerY1 = currentPos.y + itemSize / 2;const centerX2 = pos.x + itemSize / 2;const centerY2 = pos.y + itemSize / 2;const distance = Math.sqrt(Math.pow(centerX1 - centerX2, 2) +Math.pow(centerY1 - centerY2, 2));// 只考虑距离小于阈值的项const threshold = (itemSize + itemGap) * 0.6;if (distance < threshold && distance < minDistance) {minDistance = distance;closestIndex = index;}}});// 如果找到了足够近的项,交换位置if (closestIndex !== -1) {this.swapItems(this.currentIndex, closestIndex);}},// 交换两个项目swapItems(fromIndex, toIndex) {// 交换菜单列表中的项const temp = { ...this.menuList[fromIndex] };this.$set(this.menuList, fromIndex, { ...this.menuList[toIndex] });this.$set(this.menuList, toIndex, temp);// 交换位置信息[this.positions[fromIndex], this.positions[toIndex]] =[this.positions[toIndex], this.positions[fromIndex]];// 更新当前拖拽的索引this.currentIndex = toIndex;}}
}
</script><style scoped>
.container {padding: 20rpx;background-color: #f7f7f7;
}.menu-title {font-size: 32rpx;font-weight: bold;margin-bottom: 30rpx;text-align: center;
}.grid-container {position: relative;width: 100%;min-height: 500rpx;overflow: hidden;
}.grid-item {position: absolute;left: 0;top: 0;transition: transform 0.3s ease;will-change: transform;
}.grid-item.active {transition: none;transform: scale(1.05);z-index: 10;
}.item-content {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #ffffff;border-radius: 12rpx;box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}.item-icon {display: flex;justify-content: center;align-items: center;margin-bottom: 10rpx;
}.item-name {font-size: 24rpx;color: #333;text-align: center;
}
</style>

整个功能可以拆分为以下几个部分:

  1. 网格布局计算:确定每个 item 的初始位置
  2. 拖拽事件绑定:监听 touchstart / touchmove / touchend
  3. 实时移动渲染:跟随手指移动改变 transform 样式
  4. 最近距离判断:判断最近的可交换项并交换
  5. 松开后归位:释放手指后吸附至新的位置

三、组件结构设计

1. 模板部分

使用 v-for 渲染菜单项,并绑定触摸事件。

<view class="grid-item"v-for="(item, index) in menuList":key="index":class="{ 'active': currentIndex === index }":style="getPositionStyle(index)"@touchstart="handleTouchStart($event, index)"@touchmove.stop.prevent="handleTouchMove($event)"@touchend="handleTouchEnd"><!-- 图标和文字 -->
</view>
2. 数据结构
  • menuList: 菜单数据
  • positions: 所有 item 的坐标信息
  • currentIndex: 当前拖拽的索引
  • startX/Y: 拖拽起始点坐标
  • moveOffsetX/Y: 移动的累计距离
  • isDragging: 是否正在拖拽中
3. 初始化位置

通过 itemSize + itemGap + columns 计算每一项的坐标。

const row = Math.floor(index / columns);
const col = index % columns;
positions.push({x: col * (itemSize + itemGap),y: row * (itemSize + itemGap),zIndex: 1
});
4. 拖拽处理流程
- 触摸开始
  • 记录初始触摸位置
  • 提升 z-index
  • 设置当前拖拽 index
- 拖动中
  • 计算当前位置偏移量
  • 实时更新拖拽项的 transform 位置
  • 检查距离最近的其他项是否可交换
- 拖动结束
  • 重置拖拽状态
  • 吸附所有项回网格对齐
  • 发出排序完成事件
5. 交换逻辑

通过拖拽项与其它项之间的中心点距离,找到最近项,判断是否在交换阈值范围内(比如 0.6 倍 itemSize + gap),再触发 swapItems

const distance = Math.sqrt((dx)^2 + (dy)^2);
if (distance < threshold) swapItems(fromIndex, toIndex);

四、平台兼容性说明

  • 小程序端: 使用 touchstart, touchmove, touchend 原生事件即可
  • H5端: 同样支持原生事件,需使用 stop.prevent 修饰符阻止页面滚动
  • 注意事项: 不建议使用 @mousedown 等 PC 事件,移动端表现不一致

五、性能优化建议

  • 使用 transform: translate3d 提升动画性能
  • 拖拽时关闭 transition,松开后再开启
  • 将 drag 状态变化为响应式变量,避免频繁操作 DOM

六、完整效果图示例

H5端
在这里插入图片描述

小程序端
在这里插入图片描述

七、总结

本组件通过计算每个 item 的位置并绑定触摸事件,实现了拖拽排序的能力,支持吸附、交换和动态位置调整,兼容多个平台。适用于菜单管理、组件排序等场景,封装后复用性强。

如果你有更多关于 UniApp 拖拽交互的场景需求,欢迎留言讨论!

**


文章转载自:

http://3JwCrYXQ.yknsr.cn
http://WIjetAyz.yknsr.cn
http://TMzQeiom.yknsr.cn
http://nh5LekN2.yknsr.cn
http://uEjEsooy.yknsr.cn
http://CepHGZ2t.yknsr.cn
http://DiQMRPa8.yknsr.cn
http://YYSRZe7U.yknsr.cn
http://FmByhVlw.yknsr.cn
http://bWI1Bj9i.yknsr.cn
http://jgb6IqBC.yknsr.cn
http://oPUX8rJg.yknsr.cn
http://ddaL2Oru.yknsr.cn
http://SKy4U0x2.yknsr.cn
http://2yOwib7a.yknsr.cn
http://ImSJVrEm.yknsr.cn
http://XanhChD1.yknsr.cn
http://v8h0HiSK.yknsr.cn
http://SGeM9mSl.yknsr.cn
http://SDL5ebyz.yknsr.cn
http://1ycraCuI.yknsr.cn
http://xUrjykD0.yknsr.cn
http://NuFh1HVQ.yknsr.cn
http://nZnGVoXR.yknsr.cn
http://vwc0PRhq.yknsr.cn
http://aS4YODJj.yknsr.cn
http://scwWqXgW.yknsr.cn
http://3QUpS6ir.yknsr.cn
http://IID1sVdD.yknsr.cn
http://0gVbPnyv.yknsr.cn
http://www.dtcms.com/wzjs/706393.html

相关文章:

  • 网站广告设计网站建设pdf文件怎么发布
  • 网站做外链软件渭南网站建设哪里便宜
  • 精品课程网站建设设计方案网站建设的毕业报告
  • 网站建没有前景洋气的传媒公司名字
  • 网站关键词做多了是不是影响权重网站建设使用软件
  • 怎么用模板建网站国际网站浏览器
  • 网站开发宣传语海南科技职业大学教务网络管理系统
  • 网站首页动画效果wordpress迁移保留账号
  • 搜索的网站后大拇指分享数量不见了做电影下载网站成本
  • 昆山网站建设兼职上海市质量工程建设管理协会网站
  • 建设汽车行业网站旅游网站开发实验报告
  • 大庆做网站的公司石景山网站建设多少钱
  • 福田住房和建设局网站官网十大软件免费下载安装手机版
  • 网站建设便宜公司建设银行违法网站
  • 企业网站建设需注意点政务网站建设工作方案
  • 网站设置为起始页英文网站建设知识
  • 做贸易 公司网站放哪里网站建设公司的政策风险
  • 网站推广朋友圈文案怎么做网站教程
  • 网站开发是什么职业les做ml网站
  • 浦东新区专业网站建设wordpress安装过程
  • 易尔通做网站怎么样东莞大朗最新通告
  • 网站和网址的区别个人网站空间购买
  • 做ppt接单的网站电子商务的就业方向
  • 做淘宝返利网站能挣钱建设一个功能简单的网站
  • 网站建设怎么做网站网络维护工作室 员工职务
  • nodejs 做视频网站wordpress恢复已删除目录
  • 西安建立公司网站的步骤阿里云小程序开发
  • 如何不让百度收录网站南山做网站关于枪
  • 网络工程师自学网站网站后台无法修改信息
  • 做网站难度企业宣传片走心文案