效果展示
思路
- 使用movable-area作為可移動(dòng)區(qū)域,并在內(nèi)部循環(huán)渲染列表項(xiàng)view,綁定touch事件。
- 在mounted生命周期函數(shù)內(nèi)獲取區(qū)域movable-area的dom信息,記錄列表項(xiàng)的坐標(biāo)信息。
- 在methods中定義了列表項(xiàng)的touchstart、touchmove和touchend事件的方法,用于實(shí)現(xiàn)列表項(xiàng)的拖拽移動(dòng)和位置變更。
- watch監(jiān)聽(tīng)列表項(xiàng)數(shù)據(jù)listData的變化,并拋出事件,通知列表變更。
具體步驟
1, 在components文件夾新建healer-dragList文件夾,在healer-dragList文件夾下新建AppList.vue組件
使用movable-area創(chuàng)建一個(gè)可移動(dòng)區(qū)域容器
movable-area 是 uniapp 的可移動(dòng)區(qū)域組件。它用于定義可移動(dòng)視圖容器,在其內(nèi)部可拖拽移動(dòng)子視圖。
在 movable-area 組件中,可以使用 movable-view 組件定義可移動(dòng)的子視圖。movable-view 必須是 movable-area 的直接子節(jié)點(diǎn),不支持嵌套 movable-view。
movable-area
的屬性有:
- scale - 手勢(shì)縮放比例,默認(rèn)為1, 范圍0~10
- direction - 可移動(dòng)方向,值有 ‘a(chǎn)ll’,‘vertical’,‘horizontal’,‘none’
movable-view
的屬性有: - direction - 可移動(dòng)方向,同 movable-area 的 direction
- inertia - 是否啟用滾動(dòng)慣性,默認(rèn)false
- outOfBounds - 超出可移動(dòng)區(qū)域后,movable-view 的行為,可選值有 ‘none’、‘hidden’、‘bounce’
- x/y - movable-view 的位置
- damping - 阻尼系數(shù),用于控制x或y變化的動(dòng)畫和過(guò)界回彈的衰減速度。取值范圍[0, 1]。
- friction - 摩擦系數(shù),用于控制x或y變化的動(dòng)畫和過(guò)界回彈的摩擦力。取值范圍[0, 1]。
movable-area 和 movable-view 通常搭配使用,來(lái)實(shí)現(xiàn)可拖拽排序的列表效果。
//AppList.vue文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-419245.html
<template>
<view>
<!-- 可移動(dòng)區(qū)域容器 -->
<movable-area class="movarea" ref="areaBox" id="areaBox">
<!-- 這塊只是循環(huán)出固定內(nèi)容,監(jiān)聽(tīng)其元素touch事件獲取坐標(biāo) -->
<view class="appList">
<view class="app-li text-blue" v-for="(appItem,index) in listData_c" :key="appItem.name"
:id="'appLi' + index" :class="(hoverClass==='appLi'+index)?'select':''"
@touchstart="AppLi_touchstart(index,$event)" @touchmove="AppLi_touchmove"
@touchend="AppLi_touchend(index)">
<uni-icons type="minus-filled" size="20" class='rightIcon' @click="dleIcon(index,appItem)"
v-if="isEdti"></uni-icons>
<image class="appIcon" :src="appItem.appIcon" mode="widthFix"></image>
<text class="appName">{{appItem.appName}}</text>
<text class="appicon cuIcon-roundclosefill"
:class="deleteAppID===appItem.appId && showDelete?'':'hide'" @tap="deleteAppItem(index)"></text>
</view>
<view class="app-li text-blue" @tap="addAppItem">
<text class="appicon cuIcon-roundadd"></text>
</view>
</view>
<!-- 滑塊 -->
<movable-view v-if="moviewShow" :animation="false" class="moveV text-blue" :x="moveX" :y="moveY"
direction="all" :style="{ width: moveViewSize + 'px', height: 160 + 'rpx' }">
<image class="appIcon" :src="touchItem.appIcon" mode="widthFix"></image>
<text class="appName">{{touchItem.appName}}</text>
</movable-view>
</movable-area>
<!-- 新增模態(tài) -->
<!-- <Modal ref="addAppItem" title="添加" @confirm="confirm">
<InputUnify ref="addAppInput" title="名字" placeholder="請(qǐng)輸入應(yīng)用名"></InputUnify>
</Modal> -->
</view>
</template>
<script>
// 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, //觸發(fā)刪除的itemID
showDelete: false, //刪除按鈕狀態(tài)
IsDeleteAfter: false, //是否為刪除后
IsCancelDelete: false, //是否為取消后
moviewShow: false, //滑塊狀態(tài)
areaBoxInfo: null, //保存滑動(dòng)區(qū)域盒子dom信息
inBoxXY: {}, //鼠標(biāo)在item中的坐標(biāo)
touchIndex: 0, //被移動(dòng)index
touchItem: '', //備份被移動(dòng)item數(shù)據(jù)
moveX: 0, //相對(duì)滑動(dòng)盒子的坐標(biāo)
moveY: 0, //相對(duì)滑動(dòng)盒子的坐標(biāo)
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)
// }
}
// 過(guò)時(shí)觸發(fā)(touchEnd中清除此定時(shí)器)
this.Loop = setTimeout(
() => {
// 觸感反饋(安卓上是150毫秒,ios無(wú)短觸控反饋)
uni.vibrateShort();
this.showDelete = true;
this.deleteAppID = this.touchItem.appId;
// 拖動(dòng)邏輯
//顯示可移動(dòng)方塊
this.moviewShow = true
//保存當(dāng)前所選擇的索引
this.touchIndex = index;
// 設(shè)置可移動(dòng)方塊的初始位置為當(dāng)前所選中圖片的位置坐標(biāo)
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;
// 保存鼠標(biāo)在圖片內(nèi)的坐標(biāo)
this.inBoxXY = {
x: x - this.listData_c[index].x,
y: y - this.listData_c[index].y,
}
},
500);
},
AppLi_touchmove(event) {
// 每次endTouch清除startTouch刪除按鈕定時(shí)器
if (this.Loop) {
clearTimeout(this.Loop);
this.Loop = null;
}
if (this.showDelete) {
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
//重置為以拖拽盒子左上角為坐標(biāo)原點(diǎn)
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.x + 80 && y > item.y && y < item.y + 80) {
this.hoverClass = 'appLi' + idx
this.hoverClassIndex = idx;
setIng = true
}
});
// 都不存在代表脫離
if (!setIng) {
this.hoverClass = ""
this.hoverClassIndex = null;
}
}
},
AppLi_touchend(index) {
if (!this.showDelete && !this.IsDeleteAfter && !this.IsCancelDelete) {
this.getInto(this.touchItem)
} else {
// 為下次getInto清除狀態(tài)
this.IsDeleteAfter = false;
this.IsCancelDelete = false;
// 移動(dòng)結(jié)束隱藏可移動(dòng)方塊
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刪除按鈕定時(shí)器
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;
// 設(shè)置區(qū)域內(nèi)所有圖片的左上角坐標(biāo)
_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;
}
}
}
</script>
<style lang="scss" scoped>
.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;
}
</style>
2, 在所需頁(yè)面引用AppList.vue組件
<template>
<view class="content">
<!-- appList start-->
<view class="topText">
點(diǎn)擊下方【編輯】按鈕,可調(diào)整首頁(yè)功能展示
長(zhǎng)按圖標(biāo)可調(diào)整首頁(yè)圖標(biāo)展示順序
</view>
<view class="title">
首頁(yè)已展示功能
</view>
<view class="" style="padding: 0 30rpx;">
<AppList :listData="appListData" @listChange="listChange" @lowAppList='lowAppListData' :isEdti='isEdti'>
</AppList>
</view>
<view class="title">
其他功能
</view>
<view style="padding: 0 30rpx;">
<view class="appList">
<view class="app-li text-blue" v-for="(appItem,index) in autherData" :key="appItem.name">
<uni-icons type="plus-filled" size="20" class='rightIcon' @tap="addIcon(index)" v-if="isEdti">
</uni-icons>
<image class="appIcon" :src="appItem.appIcon" mode="widthFix" @tap="goAuther(appItem)"></image>
<text class="appName">{{appItem.appName}}</text>
<text class="appicon cuIcon-roundclosefill"
:class="deleteAppID===appItem.appId && showDelete?'':'hide'" @tap="deleteAppItem(index)"></text>
</view>
<view class="app-li text-blue" @tap="addAppItem">
<text class="appicon cuIcon-roundadd"></text>
</view>
</view>
</view>
<!-- appList end-->
<view class="btmBox" v-if="isEdti" @click="setMenuStor">
完成
</view>
<view class="btmBox" v-else @click="isEdti=!isEdti">
編輯
</view>
</view>
</template>
<script>
import AppList from "@/components/healer-dragList/AppList.vue"
export default {
data() {
return {
isEdti: false,
//這里寫你自己頁(yè)面路由信息
appListData: [{
appId: 0,
appName: '示例菜單跳轉(zhuǎn)頁(yè)面',
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: '當(dāng)前模塊正在開(kāi)發(fā)...',
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: '更多不能被移出首頁(yè)',
icon: 'none'
})
return
}
this.autherData.push(this.appListData[e])
this.appListData.splice(e, 1)
},
addIcon(index) {
if (this.appListData.length == 10) {
uni.showToast({
title: '首頁(yè)菜單不能大于10個(gè)',
icon: 'none',
duration: 2000
})
return;
}
this.appListData.push(this.autherData[index])
this.autherData.splice(index, 1)
},
listChange(option) {
console.log("listChange", option)
}
}
}
</script>
<style scoped lang="scss">
.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;
}
</style>
總結(jié)
以上代碼實(shí)現(xiàn)了uniapp在小程序端實(shí)現(xiàn)菜單拖拽排序,以及顯示隱藏指定菜單功能,有點(diǎn)小bug,需要原始代碼的可以給我私信留言。當(dāng)然有更簡(jiǎn)單的辦法 uni-app切片工具也可以實(shí)現(xiàn)拖拽排序、菜單排序、導(dǎo)航排序等更多功能!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-419245.html
到了這里,關(guān)于小程序封裝拖拽菜單組件(uniapp拖拽排序,自定義菜單)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!