Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

类似桌面的鼠标框选,和拖动选择的所有元素(360°的框选) #14

Open
lzpong opened this issue Nov 9, 2017 · 2 comments

Comments

@lzpong
Copy link
Owner

lzpong commented Nov 9, 2017

类似桌面的鼠标框选,和拖动选择的所有元素
亮点是实现了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);
}
*/
@lzpong
Copy link
Owner Author

lzpong commented Nov 10, 2017

对齐的八种方式

下面是css和html片段

工具条图片(你可以右键保存, js函数中的序号与图片中的图标序号一致)
image

.tool {
    background: url(/img/tools.png) no-repeat;
    width: 28px;
    height: 25px;
}
#tool1 { background-position-x:-3px }
#tool2 { background-position-x:-26px }
#tool3 { background-position-x:-48px }
#tool4 { background-position-x:-70px }
#tool5 { background-position-x:-92px }
#tool6 { background-position-x:-115px }
#tool7 { background-position-x:-26px }
#tool8 { background-position-x:-92px }
<input id="tool1" class="tool" type="button" title="左对齐"/><input id="tool2" class="tool" type="button" title="垂直等距"/><input id="tool3" class="tool" type="button" title="右对齐"/><input id="tool4" class="tool" type="button" title="上对齐"/><input id="tool5" class="tool" type="button" title="水平等距"/><input id="tool6" class="tool" type="button" title="下对齐"/><input id="tool7" class="tool" type="button" title="垂直等距"/><input id="tool8" class="tool" type="button" title="水平等距"/>

下面是js函数

function AlignSlcts(n)
{
    var slts=slectedBoxs;
    if(slts.length<1)
        return;
    var stl=0,stl2=0;
    switch(n)
    {
        case 1://左对齐
            stl=slts[0].offsetLeft;
            slts.forEach((o)=>{
                if(stl>o.offsetLeft) stl=o.offsetLeft;
            });
            slts.forEach((o)=>{
                o.style.left=stl+"px";
            });
            break;
        case 2://垂直对齐
        case 7:
            stl=stl2=slts[0].offsetTop;
            slts.forEach((o)=>{
                if(stl>o.offsetTop) stl=o.offsetTop;
                if(stl2<o.offsetTop) stl2=o.offsetTop;
            });
            stl=(stl2-stl)/(slts.length-1);
            slts.sort((a,b)=>{return a.offsetLeft-b.offsetLeft});
            for(i=1;i<slts.length;i++)
            {
                slts[i].style.top=slts[0].offsetTop+stl*i+"px";
            };
            break;
        case 3://右对齐
            stl=slts[0].offsetLeft;
            slts.forEach((o)=>{
                if(stl<o.offsetLeft) stl=o.offsetLeft;
            });
            slts.forEach((o)=>{
                o.style.left=stl+"px";
            });
            break;
        case 4://上对齐
            stl=slts[0].offsetTop;
            slts.forEach((o)=>{
                if(stl>o.offsetTop) stl=o.offsetTop;
            });
            slts.forEach((o)=>{
                o.style.top=stl+"px";
            });
            break;
        case 5://水平对齐
        case 8:
            stl=stl2=slts[0].offsetLeft;
            slts.forEach((o)=>{
                if(stl>o.offsetLeft) stl=o.offsetLeft;
                if(stl2<o.offsetLeft) stl2=o.offsetLeft;
            });
            stl=(stl2-stl)/(slts.length-1);
            slts.sort((a,b)=>{return a.offsetTop-b.offsetTop});
            for(i=1;i<slts.length;i++)
            {
                slts[i].style.left=slts[0].offsetLeft+stl*i+"px";
            };
            break;
        case 6://下对齐
            stl=slts[0].offsetTop;
            slts.forEach((o)=>{
                if(stl<o.offsetTop) stl=o.offsetTop;
            });
            slts.forEach((o)=>{
                o.style.top=stl+"px";
            });
            break;
    }
}
//绑定点击
for(i=1;i<9;i++)
    document.getElementById("tool"+i).onclick=function(){
        var n=i;
        return function(){AlignSlcts(n)}
    }();

@zhouzhili
Copy link

mark,正要做这个,学习下

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants