美文网首页
九宫格拖拽

九宫格拖拽

作者: 升龙无涯 | 来源:发表于2021-09-03 09:12 被阅读0次

    效果如下图:


    九宫格拖拽

    html结构和样式:

    <style>
    .box{
        width: 300px;
        height: 300px;
        border: 3px solid #000;
        position: relative;
    }
    <body>
    <div class="box"></div>
    

    js操作代码:
    首先定义几个工具函数,例如:批量设置样式的、获取随机颜色的等等。。。

    // 获取样式的函数
    function getStyle(ele, attr){
        if(window.getComputedStyle){
            return window.getComputedStyle(ele)[attr]
        }else{
            return ele.currentStyle[attr]
        }
    }
    // 获取随机颜色的函数
    function getColor(){
        var color = '#';
        for(var i=0;i<3;i++){
            var hex = Math.floor(Math.random() * 256).toString(16)
            hex = hex.length === 1 ? '0' + hex : hex;
            color += hex;
        }
        return color
    }
    // 设置样式的函数
    function setStyle(ele, styleObj){
        for(var attr in styleObj){
            ele.style[attr] = styleObj[attr];
        }
    }
    ···
    接下来定义一些变量,用来控制大盒子中小盒子的数量、宽度、高度、位置等。。。
    ```js
    // 获取大盒子
    var box = document.querySelector('.box');
    // 不能选中box中的内容
    box.onselectstart = function(){
        return false;
    }
    // 定义小盒子宽高
    var smallBoxWidth = 100;
    var smallBoxHeight = 100;
    // 计算小盒子行和列的数量
    var smallBoxRowNum = box.clientWidth / smallBoxWidth;
    var smallBoxColNum = box.clientHeight / smallBoxHeight;
    // 计算小盒子的数量
    var smallBoxNum = smallBoxRowNum * smallBoxColNum
    

    根据上面计算好的变量,来创建大盒子中的小盒子并设置样式:

    // 给大盒子中创建小盒子
    for(var i=0;i<smallBoxNum;i++){
        var div = document.createElement('div')
        // 将创建好的div放在大盒子中
        box.appendChild(div) 
        // 给每个小div设置样式
        setStyle(div,{
            width:"100px",
            height:"100px",
            backgroundColor: getColor(),
            position:"absolute",
            left:(i % smallBoxColNum) * smallBoxWidth + 'px',
            top: parseInt(i / smallBoxRowNum) * smallBoxHeight + 'px',
            borderRadius:'10px'
        })   
    }
    

    效果要实现拖拽,计算公共面积并交换位置,先从过拽效果开始,并记录当前正在过拽的小盒子的下标和位置:

    // 记录当前拖拽的小盒子的下标
    var index;
    // 记录当前拖拽的小盒子的left和top
    var currentPosition = {
        left:0,
        top:0
    }
    // 定义开关 - 开启状态
    var flag = true
    // 拖拽所有小盒子
    for(var i=0;i<box.children.length;i++){
        // 为了能在按下的事件中获取到当前操作的小盒子的下标 - 给事件外面嵌套自调用函数并传入参数i
        (function(i){
            // 鼠标按下事件
            box.children[i].onmousedown = function(){
                // 开关关闭状态不允许操作
                if(!flag) return false;
                // 获取按下时鼠标在小盒子上的位置
                var e = window.event;
                var x = e.offsetX;
                var y = e.offsetY;
                // 将当前小盒子的层级设置为1 - 在其他小盒子的上面
                this.style.zIndex = 1
                // 将当前按下的小盒子的下标记录起来
                index = i
                // 将当前要拖拽的小盒子的位置记录起来
                currentPosition.left = box.children[i].offsetLeft
                currentPosition.top = box.children[i].offsetTop
                box.onmousemove = function(){
                    var e = window.event;
                    var x1 = e.pageX;
                    var y1 = e.pageY;
                    var l = x1 - x - box.offsetLeft - parseInt(getStyle(box,'border-left-width'))
                    var t = y1 - y - box.offsetTop - parseInt(getStyle(box,'border-top-width'))
                    if(l<0){
                        l = 0
                    }
                    if(t<0){
                        t = 0
                    }
                    if(l>box.clientWidth - smallBoxWidth){
                        l=box.clientWidth - smallBoxWidth
                    }
                    if(t>box.clientHeight - smallBoxHeight){
                        t=box.clientHeight - smallBoxHeight
                    }
                    box.children[i].style.left = l + 'px'
                    box.children[i].style.top = t + 'px'
                }
            }
        })(i)
    }
    

    当鼠标松开的时候,计算公共面积,判断是否超过面积的一半,决定是否需要交换位置:

    // 拖拽结束松开鼠标时
    document.onmouseup = function(){
        if(index === undefined) return false;
        if(!flag) return false
        flag = false
        box.onmousemove = null
        // 记录所有小盒子跟当前小盒子的重叠面积和下标
        var arr = [];
        // 判断刚刚拖拽的小盒子跟其他小盒子,哪个重叠的面积最大
        for(var j=0;j<box.children.length;j++){
            // 重叠部分的宽
            var width = smallBoxWidth - Math.abs(box.children[index].offsetLeft - box.children[j].offsetLeft);
            // 重叠部分的高度
            var height = smallBoxHeight - Math.abs(box.children[index].offsetTop - box.children[j].offsetTop)
            // 如果遍历的小盒子的下标跟刚刚拖拽的小盒子的下标一样 - 自己跟自己算宽和高 - 就跳过
            // 如果计算出来的共同面积的宽度或共同面积的高度相等,就表示没有重叠在一起 - 就跳过
            if(j === index || width < 0 || height < 0){
                continue
            }
            // 将和刚刚拖拽的小盒子 重叠 在一起的小盒子的下标、共同宽度、共同高度、共同面积组成一个对象放在数组中
            arr.push({
                width,
                height,
                area:width * height,
                index:j
            })
        }   
        // 求出面积最大的那个div的下标
        var maxIndex = arr[0].index; // 假设共同面积最大的那个小盒子的下标是数组中小标为0那个小盒子的下标
        var maxArea = arr[0].area; // 假设共同面积最大的那个小盒子是数组中第一个小盒子的面积
        // 通过循环计算面积最大的那个对象 - 将其中那个对象中存储的下标赋值给maxIndex
        for(var j=1;j<arr.length;j++){
            if(maxArea<arr[j].area){
                maxArea = arr[j].area
                maxIndex = arr[j].index
            }
        }
        // maxIndex就是共同面积最大的小盒子的下标
        // 设置所有小盒子的过渡
        for(var j=0;j<box.children.length;j++){
            box.children[j].style.transition = 'all 2s ease';
        }
        // 设置被交换的小盒子的层级 - 降低 - 从下面穿过去
        box.children[maxIndex].style.zIndex = -10;
        // 判断公共面积是否超过一半 - 超过一半就交换位置
        if(maxArea>smallBoxWidth*smallBoxHeight/2){
            // 将maxIndex和刚刚过拽的小盒子的位置进行交换
            box.children[index].style.left = box.children[maxIndex].offsetLeft + 'px'
            box.children[index].style.top = box.children[maxIndex].offsetTop + 'px'
            box.children[maxIndex].style.left = currentPosition.left + 'px'
            box.children[maxIndex].style.top = currentPosition.top + 'px'
        }else{ // 公共面积没有超过一半就回到原来的位置
            box.children[index].style.left = currentPosition.left + 'px'
            box.children[index].style.top = currentPosition.top + 'px'
        }
        // 将index设置为undefined,方便上面判断
        index = undefined
        // 将所有小盒子的层级重新设置 - 并取消所有小盒子的过渡效果
        setTimeout(function(){ // 因为过渡效果需要2s,所以设置定时器,在2s后打开开关,表示2s后才可以进行下一次操作
            for(var j=0;j<box.children.length;j++){
                box.children[j].style.transition = null;
                box.children[j].style.zIndex = 0;
            }
            // 打开开关
            flag = true
        },2000)
    }
    

    其中给index变量赋值为undefined以及定义的开关和定时器,都是为了控制当前这个过渡效果没有结束的时候,不允许开启下次的效果

    相关文章

      网友评论

          本文标题:九宫格拖拽

          本文链接:https://www.haomeiwen.com/subject/csxfwltx.html