今天在某前端群里看到有群友提到,想到以后可能会碰到类似的场景,于是就动手敲了敲
1️⃣ eg
2️⃣ About
1. 监听鼠标的 按下 & 移动 & 抬起
onmousedown
onmousemove
onmouseup
document.onmousedown = (e) => {
document.onmousemove = (e2) => {
selectHandle(e.pageX, e.pageY, e2.pageX, e2.pageY)
}
document.onmouseup = (e3) => {
cleanSelect()
document.onmousemove = null
}
}
按下 (onmousedown) 时拿到开始坐标并记录,移动 (onmousemove) 后的结果坐标为结束坐标,在两坐标 x轴 & y轴 相交范围内的 Dom 即为需要添加选中的
鼠标抬起 (onmouseup) 时清空鼠标移动监听
2. 选中状态的添加
document.elementFromPoint(x, y)
用到了根据坐标获取 Dom元素,同时也是实现的关键
(这个函数印象中我几乎没有实际使用过 🤔
/**
* 选中处理
* @param beginX
* @param beginY
* @param endX
* @param endY
*/
const selectHandle = (beginX, beginY, endX, endY) => {
cleanSelect()
/**
* x - 记录当前列的坐标 x,在一列处理完后根据 dom元素 的(宽度 + x坐标)进行累加
* y - 记录当前行的坐标 y,在一行处理完后根据 dom元素 的(高度 + y坐标)进行累加
* isReverseY - Y轴移动方向,向上移动为 true,向下为 false
* isReverseX - X轴移动方向,向左移动为 true,向右为 false
* range - 允许的选中偏差
*/
let x = beginX, y = beginY, isReverseY = beginY > endY, isReverseX = beginX > endX, range = 10
const xHandle = () => {
const yHandle = () => {
let e = document.elementFromPoint(x, y)
if (e) {
addDomToArr(e)
y = isReverseY ? e.offsetTop - range : e.clientHeight + e.offsetTop + range
if (isReverseY ? y >= endY : y <= endY) xHandle()
}
}
let e = document.elementFromPoint(x, y)
if (e) {
yHandle()
y = beginY
x = isReverseX ? e.offsetLeft - range : e.clientWidth + e.offsetLeft + range
if (isReverseX ? x >= endX : x <= endX) xHandle()
}
}
xHandle()
// 改变选中元素的边框颜色
arr.forEach(v => {
v.style.border = '2px solid #F8CBA6'
})
}
/**
* 将dom元素添加至 `arr(已选中)`
* @param dom
*/
const addDomToArr = (dom) => {
if (dom.parentNode.getAttribute('class') != 'container') return
let has = false
arr.forEach(v => {
// 判断是否已存在
if (v.offsetLeft == dom.offsetLeft && v.offsetTop == dom.offsetTop) {
has = true
return
}
})
if (!has) arr.push(dom)
}
Source Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
</div>
</body>
<script>
let items, arr = []
window.onload = () => {
items = document.querySelector('.container').children
document.onmousedown = (e) => {
document.onmousemove = (e2) => {
selectHandle(e.pageX, e.pageY, e2.pageX, e2.pageY)
}
document.onmouseup = (e3) => {
cleanSelect()
document.onmousemove = null
}
}
}
/**
* 选中处理
* @param beginX
* @param beginY
* @param endX
* @param endY
*/
const selectHandle = (beginX, beginY, endX, endY) => {
cleanSelect()
/**
* 采用按列添加选中的方式
* eg: 从 1,1 移动至 3,3
* * -> ** -> ***
* * -> ** -> ***
* * -> ** -> ***
*
* x - 记录当前列的坐标 x,在一列处理完后根据 dom元素 的(宽度 + x坐标)进行累加
* y - 记录当前行的坐标 y,在一行处理完后根据 dom元素 的(高度 + y坐标)进行累加
* isReverseY - Y轴移动方向,向上移动为 true,向下为 false
* isReverseX - X轴移动方向,向左移动为 true,向右为 false
* range - 允许的选中偏差
*/
let x = beginX, y = beginY, isReverseY = beginY > endY, isReverseX = beginX > endX, range = 10
const xHandle = () => {
const yHandle = () => {
let e = document.elementFromPoint(x, y)
if (e) {
addDomToArr(e)
y = isReverseY ? e.offsetTop - range : e.clientHeight + e.offsetTop + range
if (isReverseY ? y >= endY : y <= endY) xHandle()
}
}
let e = document.elementFromPoint(x, y)
if (e) {
yHandle()
y = beginY
x = isReverseX ? e.offsetLeft - range : e.clientWidth + e.offsetLeft + range
if (isReverseX ? x >= endX : x <= endX) xHandle()
}
}
xHandle()
// 改变选中元素的边框颜色
arr.forEach(v => {
v.style.border = '2px solid #F8CBA6'
})
}
/**
* 将dom元素添加至 `arr(已选中)`
* @param dom
*/
const addDomToArr = (dom) => {
if (dom.parentNode.getAttribute('class') != 'container') return
let has = false
arr.forEach(v => {
// 判断是否已存在
if (v.offsetLeft == dom.offsetLeft && v.offsetTop == dom.offsetTop) {
has = true
return
}
})
if (!has) arr.push(dom)
}
/**
* 清空已选中
*/
const cleanSelect = () => {
arr = []
for (let i = 0; i < items.length; i++) {
items[i].style.border = '2px solid #FFFBEB00'
}
}
</script>
<style>
body {
display: flex;
align-items: center;
justify-content: center;
background-color: #ECF9FF;
min-width: 100vw;
min-height: 100vh;
margin: unset;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 1px;
background-color: #FFE7CC;
}
.container div {
padding: 20px 50px;
background-color: white;
user-select: none;
border: 2px solid #FFFBEB00;
color: #F99417;
}
</style>
</html>
Comments | NOTHING