Open wingmeng opened 5 years ago
> 在线 DEMO <
移动端长按网页会弹出默认菜单,这个菜单会打断长按滑动操作,被这个问题折磨了好久,后来终于找到根因解决了。避坑指南
let boxNums = 20; new Box(boxNums); // 调用
class Box { constructor(nums = 0) { this.nums = isNaN(nums) ? 0 : Math.floor(nums); // 数量 this.boxes = []; // 当前实例的 .box 元素集合 this.rangeFrame = { // 范围选框 el: null, x: 0, y: 0 }; if (nums > 0) { this.build(); } } // 在 body 元素里创建 .box 盒子 build() { // 使用文档片段,减少性能损耗 let fragment = document.createDocumentFragment(); for (let i = 0; i < this.nums; i++) { let box = document.createElement('div'); box.className = 'box'; // 阻止移动端长按时弹出系统默认菜单 box.addEventListener('touchstart', e => e.preventDefault()); this.boxes.push(box); fragment.appendChild(box); } document.body.appendChild(fragment); this.bindEvt(); } // 绑定操作事件 bindEvt() { let timer = null; let isReady = false; // 按下时 const startHandle = e => { e.stopPropagation(); let elm = e.target; // “按压”对象为当前实例中创建的 .box 盒子之一 if (!!~this.boxes.indexOf(e.target)) { timer = setTimeout(() => { let { clientX, clientY } = e; if (e.touches) { // 移动端 clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } isReady = true; elm.classList.add('active'); this.buildRangeFrame(clientX, clientY); }, 350); } else { // 点击空白处 this.boxes.map(box => box.classList.remove('active')); } }; // 拖动中 const moveHandle = e => { if (isReady) { let { clientX, clientY } = e; if (e.touches) { // 兼容移动端 clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } this.updateRangeFrame(clientX, clientY); } }; // 松起时,清空定时器(中断高亮操作) const endHandle = (e) => { e.preventDefault(); isReady = false; clearTimeout(timer); this.removeRangeFrame(); }; // 按下 window.addEventListener('touchstart', startHandle); window.addEventListener('mousedown', startHandle); // 拖动 window.addEventListener('touchmove', moveHandle); window.addEventListener('mousemove', moveHandle); // 松起 window.addEventListener('touchend', endHandle); window.addEventListener('mouseup', endHandle); } // 创建范围选框 buildRangeFrame(posX, posY) { let elm = document.createElement('i'); elm.className = 'range-frame'; elm.style.left = `${posX}px`; elm.style.top = `${posY}px`; document.body.appendChild(elm); this.rangeFrame.el = elm; this.rangeFrame.x = posX; this.rangeFrame.y = posY; } // 更新范围选框 updateRangeFrame(posX, posY) { let { el, x, y } = this.rangeFrame; if (posX < x) { // 向左反方向 el.style.left = 'auto'; el.style.right = `${window.innerWidth - x}px`; } else { el.style.left = `${x}px`; el.style.right = 'auto'; } if (posY < y) { // 向上反方向 el.style.top = 'auto'; el.style.bottom = `${window.innerHeight - y}px`; } else { el.style.top = `${y}px`; el.style.bottom = 'auto'; } // 矩形选框尺寸 el.style.width = `${Math.abs(posX - x)}px`; el.style.height = `${Math.abs(posY - y)}px`; // 获取矩形区域左上、右下坐标 // this.computeContains({ // x1: Math.min(posX, x), y1: Math.min(posY, y), // x2: Math.max(posX, x), y2: Math.max(posY, y) // }); this.computeContains(el.getBoundingClientRect()); } // 移除范围选框 removeRangeFrame() { if (this.rangeFrame.el) { document.body.removeChild(this.rangeFrame.el); this.rangeFrame.el = null; } } // 计算 box 是否包含在选框区域 computeContains(area) { this.boxes.map(box => { let { left, top, width, height } = box.getBoundingClientRect(); // 矩形碰撞检测 if ( area.left + area.width > left && left + width > area.left // 横向 && area.top + area.height > top && top + height > area.top // 纵向 ) { box.classList.add('active'); } }); } }
分享自己画的矩形碰撞检测示意图:
// zxx: 框选有bug,一旦框选经过,框选范围再变小的时候,选中态没有还原。 // wingmeng: 谢谢张老师指点,我题意理解错了……
良好
优秀、良好、一般、差劲
题目:
我的回答:
> 在线 DEMO <
分享自己画的矩形碰撞检测示意图: