公司有个需求,要求在能选中文字的前提下,对弹出的功能栏进行自定义,要求多添加几个功能,原生的实现方式可以通过<text>组件的user-select实现,这种方式无法对弹出的弹窗进行自定义,因此只能手写。,这篇文章主要记录我实现的思路。
我的思路是将单独的文字分割成数组,同时作为元素属性,监听拖动与长按事件,当时间触发时,可以通过dataset属性获取当前数组的下标,从而获取对应的文字信息以及当前选中了哪些文字,后来发现,拖动事件的元素属性并不能够随着拖动目标的变化而变化,他的dataset值时不变的,例如,从元素1开始拖动,就只能获取元素1的dataset值,无论如何拖动,这个值始终固定,不会变化,我无法获取元素的dataset值。
计算当前拖动距离与元素高度和宽度
在发现通过动态监听touchmove事件从而获取对应元素的dataset值不行后,只能通过计算当前移动事件的开始距离与结束距离,获取每个元素距离顶部与左侧的距离,从而判断哪些元素处于移动的范围内,为这些元素添加选中样式并将选中的元素信息保存。
处理完选中的问题,还需要考虑二次选中。在已经选中的基础上,需要对各种拖动范围进行处理,包括:
开始光标或结束光标向左侧或右侧拖动:修改startindex或endIndex为当前拖动元素index(通过dataset绑定)
再次选中的元素处于被选中的元素范围之内:修改endIndex为当前选中元素index。
这里只说明最关键的选中文字实现部分,还有分割字符数组、为每个元素添加对应的选中样式、长按事件处理、拖动事件结束处理都是比较简单的部分就不过多赘述,下面附上核心的代码
//节流
moveHandle: throttle(function (e) {
let _this = this;
if (_this.data.activeObj.wordIndex.length === 0) return;
this.setData({
isShowNotice: false
})
const {
pageX,
pageY
} = e[0].touches[0];
const query = _this.createSelectorQuery();
query.selectAll('.normal').boundingClientRect(res => {
let moveNode = res.find(item => pageY >= item.top && pageY <= item.top + item.height && pageX >= item.left && pageX <= item.left + item.width);
console.log(moveNode);
if (!moveNode) {
const allLimitNodes = res.filter(item => pageY > item.top + item.height && pageX > item.left + item.width);
if (allLimitNodes.length > 0) {
moveNode = allLimitNodes[allLimitNodes.length - 1]
}
}
if (moveNode) {
const {
index
} = moveNode.dataset;
const activeObj = _this.data.activeObj;
if (index <= activeObj.startIndex) {
activeObj.startIndex = index;
activeObj.wordIndex = [..._this.generateArray(activeObj.startIndex, activeObj.endIndex)];
} else if (index > activeObj.startIndex && index < activeObj.endIndex) {
activeObj.endIndex = index;
activeObj.wordIndex = [..._this.generateArray(activeObj.startIndex, activeObj.endIndex)];
} else {
activeObj.endIndex = index;
activeObj.wordIndex = [..._this.generateArray(activeObj.startIndex, activeObj.endIndex)];
};
let arr = JSON.parse(JSON.stringify(_this.data.wordArr));
arr.forEach(item => {
item.isActive = false;
if (activeObj.wordIndex.includes(item.index)) {
item.isActive = true;
}
})
_this.setData({
wordArr: arr,
activeObj: activeObj
})
}
}).exec();
}, 100),