-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Description
类似桌面的鼠标框选,和拖动选择的所有元素
亮点是实现了360°框选, 和框选边界(必须框住多少才算)
.draggable{position: absolute;}
.slct_rec{
position: absolute;
border: 1px dashed blue;
overflow: hidden;
background: rgba(239, 239, 239, 0.53);
}
.slctd{
background: blue;
opacity: 0.5;
}window.onload = function(e) {
// startX, startY 为鼠标点击时初始坐标
var startX, startY;
// diffX, diffY 为鼠标初始坐标与 拖动元素 左上角坐标之差,用于拖动
// 移动时使用了`e.movementX`和`e.movementY`替代了 diffX, diffY
//var diffX, diffY;
// 是否拖动,初始为 false
var dragging = null;
// 选择框
var slct_box=null;
//框选的元素
var slectedBoxs=[];
//文档设置不能选择文字
document.onselectstart=function (e){e.returnValue=false;}
// 鼠标按下
document.onmousedown = function(e) {
startX = e.pageX;
startY = e.pageY;
// 如果鼠标在 可拖动元素 上被按下
if(e.target!=document && e.target.classList.contains("draggable")) {
// 记住拖动的元素
dragging = e.target;
// 计算坐标差值
//diffX = startX - e.target.offsetLeft;
//diffY = startY - e.target.offsetTop;
}
else {
// 若无,在页面创建 选择框
if(!slct_box){
slct_box = document.createElement("div");
slct_box.className = "slct_rec";
document.body.appendChild(slct_box);
}
slct_box.style.top = startY + 'px';
slct_box.style.left = startX + 'px';
slct_box.style.width = '0px';
slct_box.style.height = '0px';
slct_box.style.display="block";
}
document.addEventListener("mousemove", onMouseMove, false );
document.addEventListener("mouseup", onMouseDown, false );
};
// 鼠标移动
function onMouseMove(e){
// 移动,更新 拖动元素 坐标
if(dragging)
{
var mvSlts=false;
if(slectedBoxs.length>0){//是否要拖动选择的元素们
slectedBoxs.forEach((o)=>{if(o==dragging)mvSlts=true;});
}
if(mvSlts){//拖动选择的元素们
slectedBoxs.forEach((o)=>{
o.style.top = parseInt(o.style.top)+e.movementY+ 'px';
o.style.left = parseInt(o.style.left)+e.movementX + 'px';
});
}
else{
dragging.style.top = parseInt(dragging.style.top)+e.movementY+ 'px';
dragging.style.left = parseInt(dragging.style.left)+e.movementX + 'px';
//dragging.style.top = e.pageY - diffY + 'px';
//dragging.style.left = e.pageX - diffX + 'px';
}
}
// 更新 选择框 尺寸/位置
else if(slct_box)
{
h=e.pageY - startY;
w=e.pageX - startX;
if(h<0) //Y
{
slct_box.style.top=startY+h+"px";
slct_box.style.height = -h + 'px';
}
else
slct_box.style.height = h + 'px';
if(w<0) //X
{
slct_box.style.left=startX+w+"px";
slct_box.style.width=-w+"px"
}
else
slct_box.style.width=w+"px"
}
};
// 鼠标抬起
function onMouseDown(e) {
// 释放拖动元素
if(dragging)
dragging= null;
else if(slct_box) {
// 如果长宽其一大于 20px,则回调
if(slct_box.offsetWidth > 20 || slct_box.offsetHeight > 20) {
var p=GetRecPosition(slct_box);
onSelectDiv(p);
}
slct_box.style.display="none";
}
document.removeEventListener("mousemove", onMouseMove );
document.removeEventListener("mouseup", onMouseDown );
};
//取四个顶点的坐标: [`左上角`,`右上角`, `右下角`, `左下角`] 的坐标点
function GetRecPosition(o)
{
return [
{x:o.offsetLeft, y:o.offsetTop},
{x:o.offsetLeft+o.offsetWidth, y:o.offsetTop},
{x:o.offsetLeft+o.offsetWidth, y:o.offsetTop+o.offsetHeight},
{x:o.offsetLeft, y:o.offsetTop+o.offsetHeight}
];
}
//覆盖检测,四个顶点至少有一个在范围内,或覆盖区在检测区之内(横/竖的覆盖/半覆盖)
//(被覆盖元素四个顶点坐标,覆盖元素四个顶点坐标,四角判断至少覆盖像素(相当于四角向内缩了))
function isOverOn(op, pp, off) {
var isIn = 0;
off = off || 1;
//覆盖区竖半覆盖检测区
((pp[0].x > op[0].x && pp[2].x < op[2].x) && ((pp[0].y < op[0].y && pp[2].y < op[2].y && pp[2].y > op[0].y) || (pp[0].y > op[0].y && pp[2].y > op[2].y && pp[0].y < op[2].y))) && isIn++;
if (isIn > 0) return true;
//覆盖区横半覆盖检测区
(((pp[0].x < op[0].x && pp[2].x < op[2].x && pp[2].x > op[0].x) || (pp[0].x > op[0].x && pp[2].x > op[2].x && pp[0].x < op[2].x)) && (pp[0].y > op[0].y && pp[2].y < op[2].y)) && isIn++;
if (isIn > 0) return true;
//覆盖区竖穿过检测区
((pp[0].x > op[0].x && pp[2].x < op[2].x) && (pp[0].y < op[0].y && pp[2].y > op[2].y)) && isIn++;
if (isIn > 0) return true;
//覆盖区横穿过检测区
((pp[0].x < op[0].x && pp[2].x > op[2].x) && (pp[0].y > op[0].y && pp[2].y < op[2].y)) && isIn++;
if (isIn > 0) return true;
//四个顶点
op[0].x += off; op[0].y += off;
op[1].x -= off; op[1].y += off;
op[2].x -= off; op[2].y -= off;
op[3].x += off; op[3].y -= off;
op.forEach((o) => {
((o.x >= pp[0].x + off && o.x <= pp[2].x - off) && (o.y >= pp[0].y - off && o.y <= pp[2].y - off)) && isIn++;
});
return (isIn > 0);
}
//检测覆盖,重置选择的元素列表
function onSelectDiv(pp)
{
slectedBoxs=[];
ds=document.querySelectorAll(".divb");
ds.forEach((d)=>{
//console.log(d);
var op=GetRecPosition(d);
if(isOverOn(op,pp,3)>0)
{
slectedBoxs.push(d);
if(!d.classList.contains("slctd"))
d.classList.add("slctd");
}
else if(d.classList.contains("slctd"))
d.classList.remove("slctd");
});
}
};使用如下代码测试
.divb{
width: 100px;
height: 100px;
position: absolute;
border: 1px solid;
background: #febcad;
}for(y=0;y<8;y++)
for(x=0;x<8;x++)
{
d=document.createElement("div");
d.className="divb draggable";
d.style.top=y*110+60+"px";
d.style.left=x*110+30+"px";
document.body.appendChild(d);
}
*/Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels