效果展示
![在这里插入图片描述](https://img-blog.csdnimg.cn/0001db0b598d45d88a1f57fdc656087e.gif#pic_center)
思路
使用movable-area作为可移动区域,并在内部循环渲染列表项view,绑定touch事件。在mounted生命周期函数内获取区域movable-area的dom信息,记录列表项的坐标信息。在methods中定义了列表项的touchstart、touchmove和touchend事件的方法,用于实现列表项的拖拽移动和位置变更。watch监听列表项数据listData的变化,并抛出事件,通知列表变更。
具体步骤
1, 在components文件夹新建healer-dragList文件夹,在healer-dragList文件夹下新建AppList.vue组件
![在这里插入图片描述](https://img-blog.csdnimg.cn/059db8fa18da48ae818e3dda8ffee5e2.png)
使用movable-area创建一个可移动区域容器
movable-area 是 uniapp 的可移动区域组件。它用于定义可移动视图容器,在其内部可拖拽移动子视图。 在 movable-area 组件中,可以使用 movable-view 组件定义可移动的子视图。movable-view 必须是 movable-area 的直接子节点,不支持嵌套 movable-view。
movable-area 的属性有:
scale - 手势缩放比例,默认为1, 范围0~10direction - 可移动方向,值有 ‘all’,‘vertical’,‘horizontal’,‘none’ movable-view 的属性有:direction - 可移动方向,同 movable-area 的 directioninertia - 是否启用滚动惯性,默认falseoutOfBounds - 超出可移动区域后,movable-view 的行为,可选值有 ‘none’、‘hidden’、‘bounce’x/y - movable-view 的位置damping - 阻尼系数,用于控制x或y变化的动画和过界回弹的衰减速度。取值范围[0, 1]。friction - 摩擦系数,用于控制x或y变化的动画和过界回弹的摩擦力。取值范围[0, 1]。
movable-area 和 movable-view 通常搭配使用,来实现可拖拽排序的列表效果。
//AppList.vue
{{appItem.appName}}
{{touchItem.appName}}
// import InputUnify from "@/components/unify-input.vue"
export default {
name: "AppList",
props: {
listData: {
type: Array,
default: () => {
return []
}
},
isEdti: {
type: Boolean,
default: false
}
},
data() {
return {
listData_c: this.listData, //缓存props,(不建议直接修改props)
// CheckAppId: null,
deleteAppID: null, //触发删除的itemID
showDelete: false, //删除按钮状态
IsDeleteAfter: false, //是否为删除后
IsCancelDelete: false, //是否为取消后
moviewShow: false, //滑块状态
areaBoxInfo: null, //保存滑动区域盒子dom信息
inBoxXY: {}, //鼠标在item中的坐标
touchIndex: 0, //被移动index
touchItem: '', //备份被移动item数据
moveX: 0, //相对滑动盒子的坐标
moveY: 0, //相对滑动盒子的坐标
hoverClass: '',
hoverClassIndex: null, //最终index
};
},
watch: {
listData_c(val) {
this.$emit("listChange", val)
}
},
computed: {
moveViewSize() {
if (this.areaBoxInfo && this.areaBoxInfo.width) {
return this.areaBoxInfo.width / 5
} else {
return 0
}
}
},
components: {
// InputUnify
},
mounted() {
// 获取dom信息
this.resetListDom()
},
methods: {
dleIcon(a, b) {
this.$emit("lowAppList", a);
},
getDomInfo(id, callBack) {
const query = uni.createSelectorQuery().in(this);
query.select('#' + id)
.boundingClientRect()
.exec(function(res) {
callBack(res[0]);
});
},
// 添加
addAppItem() {
this.$refs.addAppItem.ModalStatus()
},
confirm() {
let appItem = {
appId: this.listData_c.length + 1,
appIcon: "cuIcon-pic",
appName: this.$refs.addAppInput.value,
appLink: ""
};
this.listData_c.push(appItem);
this.$refs.addAppInput.resetVal();
this.$nextTick(() => {
this.resetListDom()
});
},
AppLi_touchstart(index, event) {
this.touchItem = this.listData_c[index];
// 行为判断
if (this.showDelete) {
// 取消删除
if (this.touchItem.appId != this.deleteAppID) {
this.deleteAppID = null;
this.showDelete = false;
this.IsCancelDelete = true;
}
// 删除
// if(this.touchItem.appId==this.deleteAppID){
// this.deleteAppItem(index)
// }
}
// 过时触发(touchEnd中清除此定时器)
this.Loop = setTimeout(
() => {
// 触感反馈(安卓上是150毫秒,ios无短触控反馈)
uni.vibrateShort();
this.showDelete = true;
this.deleteAppID = this.touchItem.appId;
// 拖动逻辑
//显示可移动方块
this.moviewShow = true
//保存当前所选择的索引
this.touchIndex = index;
// 设置可移动方块的初始位置为当前所选中图片的位置坐标
this.moveX = this.listData_c[index].x;
this.moveY = this.listData_c[index].y;
var x = event.changedTouches[0].clientX - this.areaBoxInfo.left;
var y = event.changedTouches[0].clientY - this.areaBoxInfo.top;
// 保存鼠标在图片内的坐标
this.inBoxXY = {
x: x - this.listData_c[index].x,
y: y - this.listData_c[index].y,
}
},
500);
},
AppLi_touchmove(event) {
// 每次endTouch清除startTouch删除按钮定时器
if (this.Loop) {
clearTimeout(this.Loop);
this.Loop = null;
}
if (this.showDelete) {
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
//重置为以拖拽盒子左上角为坐标原点
var x = event.changedTouches[0].clientX - areaBoxLeft;
var y = event.changedTouches[0].clientY - areaBoxTop;
this.moveX = x - this.inBoxXY.x;
this.moveY = y - this.inBoxXY.y;
let setIng = false;
this.listData_c.forEach((item, idx) => {
if (x > item.x && x item.y && y
this.hoverClass = ""
this.hoverClassIndex = null;
}
}
},
AppLi_touchend(index) {
if (!this.showDelete && !this.IsDeleteAfter && !this.IsCancelDelete) {
this.getInto(this.touchItem)
} else {
// 为下次getInto清除状态
this.IsDeleteAfter = false;
this.IsCancelDelete = false;
// 移动结束隐藏可移动方块
if (this.hoverClassIndex != null && this.touchIndex != this.hoverClassIndex) {
this.$set(this.listData_c, this.touchIndex, this.listData_c[this.hoverClassIndex]);
this.$set(this.listData_c, this.hoverClassIndex, this.touchItem);
this.showDelete = false;
this.resetListDom()
}
this.touchItem = ""
this.moviewShow = false
this.hoverClass = ""
this.hoverClassIndex = null;
}
// 每次endTouch清除startTouch删除按钮定时器
if (this.Loop) {
clearTimeout(this.Loop);
this.Loop = null;
}
},
deleteAppItem(index) {
this.listData_c.splice(index, 1)
this.showDelete = false;
this.checkIndex = null;
this.IsDeleteAfter = true;
this.resetListDom()
},
getInto(e) {
if (e.appName == '更多') {
return;
}
if (this.isEdti) return;
uni.navigateTo({
url: e.appLink,
})
},
resetListDom() {
let _this = this;
this.getDomInfo('areaBox', info => {
_this.areaBoxInfo = info;
// 设置区域内所有图片的左上角坐标
_this.listData_c.forEach((item, idx) => {
_this.getDomInfo('appLi' + idx, res => {
item.x = res.left - info.left;
item.y = res.top - info.top;
});
});
});
},
boxClick() {
this.deleteAppID = null;
this.showDelete = false;
}
}
}
.rightIcon {
position: absolute;
right: 5rpx;
top: -10rpx;
}
.movarea {
width: 100%;
height: auto;
}
.appList {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.app-li {
width: 20%;
// height: 160rpx;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-around;
position: relative;
margin-bottom: 30rpx;
.appIcon {
font-size: 60rpx;
width: 50%;
margin: 0 auto;
}
.appName {
font-size: 24rpx;
}
.cuIcon-roundadd {
font-size: 60rpx;
color: #CCCCCC;
}
.cuIcon-roundclosefill {
position: absolute;
top: 12rpx;
right: 12rpx;
font-size: 36rpx;
z-index: 2;
&.hide {
display: none;
}
}
}
.moveV {
opacity: 0.8;
z-index: 999;
width: 100rpx;
height: 160rpx;
box-sizing: border-box;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 20rpx;
.appIcon {
font-size: 60rpx;
width: 100%;
}
.appName {
font-size: 24rpx;
}
}
.select {
// transform: scale(1.3);
border-radius: 16rpx;
border: 1px dashed #C0C0C0;
color: #C0C0C0;
}
2, 在所需页面引用AppList.vue组件
点击下方【编辑】按钮,可调整首页功能展示
长按图标可调整首页图标展示顺序
首页已展示功能
其他功能
{{appItem.appName}}
完成
编辑
import AppList from "@/components/healer-dragList/AppList.vue"
export default {
data() {
return {
isEdti: false,
//这里写你自己页面路由信息
appListData: [{
appId: 0,
appName: '示例菜单跳转页面',
appIcon: '/static/img/category/invitation.png',
appLink: "/pagesA/inviteAgents/inviteAgents"
}],
}
},
components: {
AppList,
},
onLoad() {
},
onShow() {
if (!uni.getStorageSync('MENU_DATA')) {
// this.getUseInfoData()
console.log('')
} else {
let data = uni.getStorageSync('MENU_DATA')
this.appListData = JSON.parse(data)
}
if (!uni.getStorageSync('MENU_BTM_DATA')) {
// this.getUseInfoData()
console.log('')
} else {
let data = uni.getStorageSync('MENU_BTM_DATA')
this.autherData = JSON.parse(data)
}
},
methods: {
goAuther(e) {
if (e.appName == '更多') {
return;
}
try {
uni.navigateTo({
url: e.appLink
})
} catch (err) {
uni.showToast({
title: '当前模块正在开发...',
icon: 'none'
})
}
},
setMenuStor() {
this.isEdti = false
uni.setStorageSync('MENU_DATA', JSON.stringify(this.appListData))
uni.setStorageSync('MENU_BTM_DATA', JSON.stringify(this.autherData))
},
//菜单上到下
lowAppListData(e) {
if (this.appListData[e].appName == '更多') {
uni.showToast({
title: '更多不能被移出首页',
icon: 'none'
})
return
}
this.autherData.push(this.appListData[e])
this.appListData.splice(e, 1)
},
addIcon(index) {
if (this.appListData.length == 10) {
uni.showToast({
title: '首页菜单不能大于10个',
icon: 'none',
duration: 2000
})
return;
}
this.appListData.push(this.autherData[index])
this.autherData.splice(index, 1)
},
listChange(option) {
console.log("listChange", option)
}
}
}
.rightIcon {
position: absolute;
right: 5rpx;
top: -10rpx;
}
.btmBox {
position: absolute;
bottom: 0;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #427ce7;
text-align: center;
color: white;
}
.appList {
width: 100%;
display: flex;
flex-wrap: wrap;
}
.app-li {
width: 20%;
// height: 160rpx;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-around;
position: relative;
margin-bottom: 30rpx;
.appIcon {
font-size: 60rpx;
width: 50%;
margin: 0 auto;
}
.appName {
font-size: 24rpx;
}
.cuIcon-roundadd {
font-size: 60rpx;
color: #CCCCCC;
}
.cuIcon-roundclosefill {
position: absolute;
top: 12rpx;
right: 12rpx;
font-size: 36rpx;
z-index: 2;
&.hide {
display: none;
}
}
}
.topText {
color: #427ce7;
font-size: 30rpx;
text-align: center;
padding: 50rpx;
}
.content {
background-color: #ffffff;
}
.title {
padding: 30rpx;
color: #808fb4;
}
总结
以上代码实现了uniapp在小程序端实现菜单拖拽排序,以及显示隐藏指定菜单功能,有点小bug,需要原始代码的可以给我私信留言。当然有更简单的办法 uni-app切片工具也可以实现拖拽排序、菜单排序、导航排序等更多功能!
|