自測(cè)兼容蘋果安卓端兼容無(wú)長(zhǎng)按上下跑的bug
碼云地址在文末努释,喜歡的話給個(gè)星敦捧, 拜托啦!胳赌!
效果如圖
darg.png
無(wú)拖拽滑動(dòng)橡皮筋效果問(wèn)題
Component({
options: {
multipleSlots: true,
addGlobalClass: true
},
properties: {
// 數(shù)據(jù)源
listData: {
type: Array,
value: [],
observer(data) {
{}
}
},
// 列數(shù)
columns: {
type: Number,
value: 1,
observer: 'dataChange'
},
// 頂部高度
topSize: {
type: Number,
value: 0,
observer: 'dataChange'
},
// 底部高度
bottomSize: {
type: Number,
value: 0,
observer: 'dataChange'
}
},
data: {
/* 渲染數(shù)據(jù) */
windowHeight: 0, // 視窗高度
platform: '', // 平臺(tái)信息
realTopSize: 0, // 計(jì)算后頂部高度實(shí)際值
realBottomSize: 0, // 計(jì)算后底部高度實(shí)際值
itemDom: {
// 每一項(xiàng) item 的 dom 信息, 由于大小一樣所以只存儲(chǔ)一個(gè)
width: 0,
height: 0,
left: 0,
top: 0
},
itemWrapDom: {
// 整個(gè)拖拽區(qū)域的 dom 信息
width: 0,
height: 0,
left: 0,
top: 0
},
startTouch: {
// 初始觸摸點(diǎn)信息
pageX: 0,
pageY: 0,
identifier: 0
},
/* 未渲染數(shù)據(jù) */
list: [],
cur: -1, // 當(dāng)前激活的元素
curZ: -1, // 當(dāng)前激活的元素, 用于控制激活元素z軸顯示
tranX: 0, // 當(dāng)前激活元素的 X軸 偏移量
tranY: 0, // 當(dāng)前激活元素的 Y軸 偏移量
itemWrapHeight: 0, // 動(dòng)態(tài)計(jì)算父級(jí)元素高度
dragging: false, // 是否在拖拽中
overOnePage: false, // 整個(gè)區(qū)域是否超過(guò)一個(gè)屏幕
itemTransition: false, // item 變換是否需要過(guò)渡動(dòng)畫, 首次渲染不需要,
jialength: 0
startTranX: 0, // 當(dāng)前激活元素的初始 X軸 偏移量
startTranY: 0, // 當(dāng)前激活元素的初始 Y軸 偏移量
preOriginKey: -1, // 前一次排序時(shí)候的起始 key 值
},
methods: {
/**
* 點(diǎn)擊每一項(xiàng)后觸發(fā)事件
*/
itemClick(e) {
let {
index
} = e.currentTarget.dataset
let item = this.data.list[index]
this.triggerEvent('click', {
oldKey: index,
newKey: item.key,
data: item.data
})
},
/**
* 長(zhǎng)按觸發(fā)移動(dòng)排序
*/
longPress(e) {
// 獲取觸摸點(diǎn)信息
let startTouch = e.changedTouches[0]
if (!startTouch) return
// 如果是固定項(xiàng)則返回
let index = e.currentTarget.dataset.index
// 防止多指觸發(fā) drag 動(dòng)作, 如果已經(jīng)在 drag 中則返回, touchstart 事件中有效果
if (this.data.dragging) return
this.setData({
dragging: true
})
let {
pageX: startPageX,
pageY: startPageY
} = startTouch, {
itemDom,
itemWrapDom
} = this.data,
startTranX = 0,
startTranY = 0
if (this.data.columns > 1) {
// 多列的時(shí)候計(jì)算X軸初始位移, 使 item 水平中心移動(dòng)到點(diǎn)擊處
startTranX = startPageX - itemDom.width / 2 - itemWrapDom.left
}
// 計(jì)算Y軸初始位移, 使 item 垂直中心移動(dòng)到點(diǎn)擊處
startTranY = startPageY - itemDom.height / 2 - itemWrapDom.top
this.setData({
startTouch: startTouch,
startTranX: startTranX,
startTranY: startTranY,
cur: index,
curZ: index,
tranX: startTranX,
tranY: startTranY
})
wx.vibrateShort()
},
touchMove(e) {
// // 獲取觸摸點(diǎn)信息
let currentTouch = e.changedTouches[0]
if (!currentTouch) return
if (!this.data.dragging) return
let {
windowHeight,
realTopSize,
realBottomSize,
itemDom,
startTouch,
startTranX,
startTranY,
preOriginKey
} = this.data, {
pageX: startPageX,
pageY: startPageY,
identifier: startId
} = startTouch, {
pageX: currentPageX,
pageY: currentPageY,
identifier: currentId,
clientY: currentClientY
} = currentTouch
// 如果不是同一個(gè)觸發(fā)點(diǎn)則返回
if (startId !== currentId) return
// 通過(guò) 當(dāng)前坐標(biāo)點(diǎn), 初始坐標(biāo)點(diǎn), 初始偏移量 來(lái)計(jì)算當(dāng)前偏移量
let tranX = currentPageX - startPageX + startTranX,
tranY = currentPageY - startPageY + startTranY
// 單列時(shí)候X軸初始不做位移
if (this.data.columns === 1) tranX = 0
// 判斷是否超過(guò)一屏幕, 超過(guò)則需要判斷當(dāng)前位置動(dòng)態(tài)滾動(dòng)page的位置
if (this.data.overOnePage) {
if (currentClientY > windowHeight - itemDom.height - realBottomSize) {
// 當(dāng)前觸摸點(diǎn)pageY + item高度 - (屏幕高度 - 底部固定區(qū)域高度)
wx.pageScrollTo({
scrollTop: currentPageY + itemDom.height - (windowHeight - realBottomSize),
duration: 300
})
} else if (currentClientY < itemDom.height + realTopSize) {
// 當(dāng)前觸摸點(diǎn)pageY - item高度 - 頂部固定區(qū)域高度
wx.pageScrollTo({
scrollTop: currentPageY - itemDom.height - realTopSize,
duration: 300
})
}
}
// // 設(shè)置當(dāng)前激活元素偏移量
this.setData({
tranX: tranX,
tranY: tranY
})
},
touchEnd(e) {
if (!this.data.dragging) return
// 獲取觸摸點(diǎn)信息
let currentTouch = e.changedTouches[0]
if (!currentTouch) return
if (!this.data.dragging) return
let {
windowHeight,
realTopSize,
realBottomSize,
itemDom,
startTouch,
startTranX,
startTranY,
preOriginKey
} = this.data, {
pageX: startPageX,
pageY: startPageY,
identifier: startId
} = startTouch, {
pageX: currentPageX,
pageY: currentPageY,
identifier: currentId,
clientY: currentClientY
} = currentTouch
// 如果不是同一個(gè)觸發(fā)點(diǎn)則返回
if (startId !== currentId) return
// 通過(guò) 當(dāng)前坐標(biāo)點(diǎn), 初始坐標(biāo)點(diǎn), 初始偏移量 來(lái)計(jì)算當(dāng)前偏移量
let tranX = currentPageX - startPageX + startTranX,
tranY = currentPageY - startPageY + startTranY
// 單列時(shí)候X軸初始不做位移
if (this.data.columns === 1) tranX = 0
// 獲取 originKey 和 endKey
let originKey = parseInt(e.currentTarget.dataset.key),
endKey = this.calculateMoving(tranX, tranY)
// 防止拖拽過(guò)程中發(fā)生亂序問(wèn)題
if (originKey === endKey || preOriginKey === originKey) return
this.setData({
preOriginKey: originKey
})
this.insert(originKey, endKey)
this.clearData()
},
/**
* 根據(jù)當(dāng)前的手指偏移量計(jì)算目標(biāo)key
*/
calculateMoving(tranX, tranY) {
let {
itemDom
} = this.data
let rows = Math.ceil(this.data.list.length / this.data.columns) - 1,
i = Math.round(tranX / itemDom.width),
j = Math.round(tranY / itemDom.height)
i = i > this.data.columns - 1 ? this.data.columns - 1 : i
i = i < 0 ? 0 : i
j = j < 0 ? 0 : j
j = j > rows ? rows : j
let endKey = i + this.data.columns * j
endKey =
endKey >= this.data.list.length ? this.data.list.length - 1 : endKey
return endKey
},
/**
* 根據(jù)起始key和目標(biāo)key去重新計(jì)算每一項(xiàng)的新的key
*/
// 交換數(shù)組位置
swapArr(arr, index1, index2) {
arr[index1] = arr.splice(index2, 1, arr[index1])[0]
return arr
},
insert(origin, end) {
this.setData({
itemTransition: true
})
console.log('origin', origin)
console.log('end', end)
let list
if (origin < end) {
// 正序拖動(dòng)
list = this.swapArr(this.data.list, origin, end)
this.del(list)
console.log('正序拖動(dòng)', list)
this.getPosition(list)
} else if (origin > end) {
// 倒序拖動(dòng)
list = this.swapArr(this.data.list, end, origin)
this.del(list)
console.log('倒序拖動(dòng)', list)
// this.del()
this.getPosition(list)
}
},
del(list) {
for (let i = 0; i < list.length; i = i + 2) {
if (list[i].data.nickName == '' && list[i + 1].data.nickName == '') {
list.splice(i, 2)
}
}
return list
},
/**
* 正序拖動(dòng) key 值和固定項(xiàng)判斷邏輯
*/
l2r(key, origin) {
if (key === origin) return origin
if (this.data.list[key].fixed) {
return this.l2r(key - 1, origin)
} else {
return key
}
},
/**
* 倒序拖動(dòng) key 值和固定項(xiàng)判斷邏輯
*/
r2l(key, origin) {
if (key === origin) return origin
if (this.data.list[key].fixed) {
return this.r2l(key + 1, origin)
} else {
return key
}
},
/**
* 根據(jù)排序后 list 數(shù)據(jù)進(jìn)行位移計(jì)算
*/
getPosition(data, vibrate = true) {
let that = this
let temp = []
let {
platform,
itemDom
} = this.data
let list = data.map((item, index) => {
item.tranX = itemDom.width * (index % this.data.columns)
item.tranY = Math.floor(index / this.data.columns) * itemDom.height
return item
})
this.setData({
list: list
})
if (!vibrate) return
if (platform !== 'devtools') wx.vibrateShort()
console.log('list', list);
list.map((item, index) => {
temp[index] = item.data
temp[index].tranX = item.tranX
temp[index].tranY = item.tranY
})
// 傳出的數(shù)據(jù)
let jialength = Math.floor(temp.length / 2)
console.log('加號(hào)長(zhǎng)度', jialength);
this.setData({
jialength: jialength
})
this.triggerEvent('change', {
listData: temp,
})
},
/**
* 清除參數(shù)
*/
clearData() {
this.setData({
preOriginKey: -1,
dragging: false,
cur: -1,
tranX: 0,
tranY: 0
})
// 延遲清空
setTimeout(() => {
this.setData({
curZ: -1
})
}, 300)
},
/**
* 初始化獲取 dom 信息
*/
initDom() {
wx.pageScrollTo({
scrollTop: 0,
duration: 0
})
let {
windowWidth,
windowHeight,
platform
} = wx.getSystemInfoSync()
let remScale = (windowWidth || 375) / 375,
realTopSize = (this.data.topSize * remScale) / 2,
realBottomSize = (this.data.bottomSize * remScale) / 2
this.setData({
windowHeight: windowHeight,
platform: platform,
realTopSize: realTopSize,
realBottomSize: realBottomSize
})
this.createSelectorQuery()
.select('.item')
.boundingClientRect(res => {
let rows = Math.ceil(this.data.list.length / this.data.columns)
this.setData({
itemDom: res,
itemWrapHeight: rows * res.height
})
this.getPosition(this.data.list, false)
this.createSelectorQuery()
.select('.item-wrap')
.boundingClientRect(res => {
// (列表的底部到頁(yè)面頂部距離 > 屏幕高度 - 底部固定區(qū)域高度) 用該公式來(lái)計(jì)算是否超過(guò)一頁(yè)
let overOnePage = res.bottom > windowHeight - realBottomSize
this.setData({
itemWrapDom: res,
overOnePage: overOnePage
})
console.log('overOnePage'.overOnePage);
})
.exec()
})
.exec()
},
/**
* 初始化
*/
init() {
this.clearData()
// 避免獲取不到節(jié)點(diǎn)信息報(bào)錯(cuò)問(wèn)題
if (this.data.listData.length === 0) {
this.setData({
list: []
})
return
}
// 遍歷數(shù)據(jù)源增加擴(kuò)展項(xiàng), 以用作排序使用
let list = this.data.listData.map((item, index) => {
return {
key: index,
tranX: 0,
tranY: 0,
data: item
}
})
this.setData({
list: list,
jialength: list.length / 2,
itemTransition: false
})
setTimeout(() => this.initDom(), 50)
}
},
ready() {
this.init()
}
})
gitee 碼云地址 https://gitee.com/xiamengmeng/wx-ui.git