自测兼容苹果安卓端兼容无长按上下跑的bug
码云地址在文末,喜欢的话给个星, 拜托啦!!
效果如图
darg.png
无拖拽滑动橡皮筋效果问题
Component({
options: {
multipleSlots: true,
addGlobalClass: true
},
properties: {
// 数据源
listData: {
type: Array,
value: [],
observer(data) {
{}
}
},
// 列数
columns: {
type: Number,
value: 1,
observer: 'dataChange'
},
// 顶部高度
topSize: {
type: Number,
value: 0,
observer: 'dataChange'
},
// 底部高度
bottomSize: {
type: Number,
value: 0,
observer: 'dataChange'
}
},
data: {
/* 渲染数据 */
windowHeight: 0, // 视窗高度
platform: '', // 平台信息
realTopSize: 0, // 计算后顶部高度实际值
realBottomSize: 0, // 计算后底部高度实际值
itemDom: {
// 每一项 item 的 dom 信息, 由于大小一样所以只存储一个
width: 0,
height: 0,
left: 0,
top: 0
},
itemWrapDom: {
// 整个拖拽区域的 dom 信息
width: 0,
height: 0,
left: 0,
top: 0
},
startTouch: {
// 初始触摸点信息
pageX: 0,
pageY: 0,
identifier: 0
},
/* 未渲染数据 */
list: [],
cur: -1, // 当前激活的元素
curZ: -1, // 当前激活的元素, 用于控制激活元素z轴显示
tranX: 0, // 当前激活元素的 X轴 偏移量
tranY: 0, // 当前激活元素的 Y轴 偏移量
itemWrapHeight: 0, // 动态计算父级元素高度
dragging: false, // 是否在拖拽中
overOnePage: false, // 整个区域是否超过一个屏幕
itemTransition: false, // item 变换是否需要过渡动画, 首次渲染不需要,
jialength: 0
startTranX: 0, // 当前激活元素的初始 X轴 偏移量
startTranY: 0, // 当前激活元素的初始 Y轴 偏移量
preOriginKey: -1, // 前一次排序时候的起始 key 值
},
methods: {
/**
* 点击每一项后触发事件
*/
itemClick(e) {
let {
index
} = e.currentTarget.dataset
let item = this.data.list[index]
this.triggerEvent('click', {
oldKey: index,
newKey: item.key,
data: item.data
})
},
/**
* 长按触发移动排序
*/
longPress(e) {
// 获取触摸点信息
let startTouch = e.changedTouches[0]
if (!startTouch) return
// 如果是固定项则返回
let index = e.currentTarget.dataset.index
// 防止多指触发 drag 动作, 如果已经在 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) {
// 多列的时候计算X轴初始位移, 使 item 水平中心移动到点击处
startTranX = startPageX - itemDom.width / 2 - itemWrapDom.left
}
// 计算Y轴初始位移, 使 item 垂直中心移动到点击处
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) {
// // 获取触摸点信息
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
// 如果不是同一个触发点则返回
if (startId !== currentId) return
// 通过 当前坐标点, 初始坐标点, 初始偏移量 来计算当前偏移量
let tranX = currentPageX - startPageX + startTranX,
tranY = currentPageY - startPageY + startTranY
// 单列时候X轴初始不做位移
if (this.data.columns === 1) tranX = 0
// 判断是否超过一屏幕, 超过则需要判断当前位置动态滚动page的位置
if (this.data.overOnePage) {
if (currentClientY > windowHeight - itemDom.height - realBottomSize) {
// 当前触摸点pageY + item高度 - (屏幕高度 - 底部固定区域高度)
wx.pageScrollTo({
scrollTop: currentPageY + itemDom.height - (windowHeight - realBottomSize),
duration: 300
})
} else if (currentClientY < itemDom.height + realTopSize) {
// 当前触摸点pageY - item高度 - 顶部固定区域高度
wx.pageScrollTo({
scrollTop: currentPageY - itemDom.height - realTopSize,
duration: 300
})
}
}
// // 设置当前激活元素偏移量
this.setData({
tranX: tranX,
tranY: tranY
})
},
touchEnd(e) {
if (!this.data.dragging) return
// 获取触摸点信息
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
// 如果不是同一个触发点则返回
if (startId !== currentId) return
// 通过 当前坐标点, 初始坐标点, 初始偏移量 来计算当前偏移量
let tranX = currentPageX - startPageX + startTranX,
tranY = currentPageY - startPageY + startTranY
// 单列时候X轴初始不做位移
if (this.data.columns === 1) tranX = 0
// 获取 originKey 和 endKey
let originKey = parseInt(e.currentTarget.dataset.key),
endKey = this.calculateMoving(tranX, tranY)
// 防止拖拽过程中发生乱序问题
if (originKey === endKey || preOriginKey === originKey) return
this.setData({
preOriginKey: originKey
})
this.insert(originKey, endKey)
this.clearData()
},
/**
* 根据当前的手指偏移量计算目标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
},
/**
* 根据起始key和目标key去重新计算每一项的新的key
*/
// 交换数组位置
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) {
// 正序拖动
list = this.swapArr(this.data.list, origin, end)
this.del(list)
console.log('正序拖动', list)
this.getPosition(list)
} else if (origin > end) {
// 倒序拖动
list = this.swapArr(this.data.list, end, origin)
this.del(list)
console.log('倒序拖动', 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
},
/**
* 正序拖动 key 值和固定项判断逻辑
*/
l2r(key, origin) {
if (key === origin) return origin
if (this.data.list[key].fixed) {
return this.l2r(key - 1, origin)
} else {
return key
}
},
/**
* 倒序拖动 key 值和固定项判断逻辑
*/
r2l(key, origin) {
if (key === origin) return origin
if (this.data.list[key].fixed) {
return this.r2l(key + 1, origin)
} else {
return key
}
},
/**
* 根据排序后 list 数据进行位移计算
*/
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
})
// 传出的数据
let jialength = Math.floor(temp.length / 2)
console.log('加号长度', jialength);
this.setData({
jialength: jialength
})
this.triggerEvent('change', {
listData: temp,
})
},
/**
* 清除参数
*/
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 => {
// (列表的底部到页面顶部距离 > 屏幕高度 - 底部固定区域高度) 用该公式来计算是否超过一页
let overOnePage = res.bottom > windowHeight - realBottomSize
this.setData({
itemWrapDom: res,
overOnePage: overOnePage
})
console.log('overOnePage'.overOnePage);
})
.exec()
})
.exec()
},
/**
* 初始化
*/
init() {
this.clearData()
// 避免获取不到节点信息报错问题
if (this.data.listData.length === 0) {
this.setData({
list: []
})
return
}
// 遍历数据源增加扩展项, 以用作排序使用
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