九宫格拖拽

2021-09-03  本文已影响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以及定义的开关和定时器,都是为了控制当前这个过渡效果没有结束的时候,不允许开启下次的效果

上一篇下一篇

猜你喜欢

热点阅读