九宫格拖拽
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
以及定义的开关和定时器,都是为了控制当前这个过渡效果没有结束的时候,不允许开启下次的效果