使用interact.js实现拖拽及碰撞检测功能
程序员文章站
2022-06-23 11:38:44
使用interact.js实现拖拽及碰撞检测功能需求实现代码如下生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入需求可在一个div块中添加多个拖拽分区且各分区不可重叠。实现实现需要引入interact.js文件进行开发代码如下html代码如下(项目已经搞完了,截取的拿出来的 大家见谅,缺的地方大家自己补齐吧):...
需求
可在一个div块中添加多个可拖拽可缩放分区且各分区不可重叠。
实现
- 实现需要引入interact.js文件进行开发,网上也搜索了挺多,但大多都没有碰撞检测的功能,最后借鉴vue的带有的碰撞检测的组件draggable(新版的,旧版也没有),自己写了一个。
代码如下
html代码如下(项目已经搞完了,截取的拿出来的 大家见谅,缺的地方大家自己补齐吧):
<style>
.el-form-item__content {
line-height: 40px;
position: relative;
font-size: 1em;
}
.cardlayout {
display: inline-block;
overflow: hidden;
position: relative;
}
.lm-seat-wrap {
position: relative;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
-webkit-transform-origin: left top;
transform-origin: left top;
}
.drag-div {
border: 1px dashed #626ed4;
background-color: #fff;
width: 200px;
height: 200px;
}
.drag-active-div {
border: 1px solid #d4d9ff;
background-color: #d4d9ff;
}
.handle, .vdr {
position: absolute;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.el-input {
position: relative;
font-size: 1em;
display: inline-block;
width: 100%;
}
.el-button--text {
color: #626ED4;
background: 0 0;
padding-left: 0;
padding-right: 0;
}
.el-button--text, .el-button--text.is-disabled, .el-button--text.is-disabled:focus, .el-button--text.is-disabled:hover, .el-button--text:active {
border-color: transparent;
}
.el-button {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: #FFF;
border: 1px solid #DCDFE6;
color: #606266;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: .1s;
transition: .1s;
font-weight: 500;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
}
<!-- 我的拖拽分区在modal拟态框,解决在拟态框上拖拽异常的问题 -->
#partitionParentDiv * {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
#partitionParentDiv *:before,
#partitionParentDiv *:after {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
</style>
<div class="" style="text-align:center;">
<button type="button" class="btn btn-primary drag_l">新增分区</button>
<button type="button" class="btn btn-primary" onclick="delPartition()">删除分区</button>
</div>
<div style="text-align:center;">
<div id="partitionDiv" class="cardlayout"
style="border: 8px solid rgb(221, 221, 221); border-radius: 8px; margin-top: 10px;">
</div>
</div>
js代码如下:
//分区坐标对象
function createPrent(id) {
var parentDiv = {
num: id,
x: 0,
y: 0,
w: 200,
h: 200
};
return parentDiv;
}
//所有分区坐标对象集合
var allparents = new Array();
//当前元素的x与y坐标
var curX = "";
var curY = "";
var curW = "";
var curH = "";
//当前选中分区
var curSelPartition = "";
//分区编号
var partitionNumber = 0;
/***
* 外层div初始化大小显示
* @param resolvingPower div分辨率大小如 1080.0*1920.0形式
*/
function initConfigPartition(resolvingPower) {
var maxsize = 1000;
var resolvingPowerArr = resolvingPower.split("*");
//放置分辨率太大百分去缩放比例
var scale = maxsize / resolvingPowerArr[0];
var marginRight = maxsize - resolvingPowerArr[0];
var marginBottom = (resolvingPowerArr[1] * scale) - resolvingPowerArr[1];
$("#partitionDiv").append("<div id='partitionParentDiv' class='right lm-seat-wrap' style='transform: scale(" + scale + "); width: " + resolvingPowerArr[0] + "px; height: " + resolvingPowerArr[1] + "px;margin-right: " + marginRight + "px;margin-bottom: " + marginBottom + "px;'></div>");
}
(function () {
$(function () {
//添加分区按钮触发
$('.drag_l').click(function () {
//首次添加
//分区编号
partitionNumber++;
//创建分区对象
var partition = createPrent(partitionNumber);
//分区维护集合
allparents.push(partition);
//碰撞检测
if(!checkConflict(partition)){
alert("请留出增加分区的区域")
//将重叠分区进行删除 curSelPartition为当前选中分区
curSelPartition = partitionNumber;
$("#"+partitionNumber).remove();
removeParent(partitionNumber);
//初始化当前选中分区的值
curSelPartition = "";
return;
}
$('.right').append('<div class="draggable drag-div vdr" id="' + partitionNumber + '">\n' +
' <h4 class="fontColor">区域' + partitionNumber + '</h4>\n' +
' <p class="lt fontColor"></p>\n' +
' <p class="wh fontColor"></p>\n' +
' </div>')
//支付区域增加响应选项
$("#partitionQrcode").append("<option value='" + partitionNumber + "'>区域" + partitionNumber + "</option>");
//为分区绑定选中事件
$("#"+partitionNumber).bind("click",function () {
curSelPartition = event.target.id;
if(!$($(event.target)).hasClass("drag-active-div")){
$($(event.target)).addClass("drag-active-div");
}
for(var i = 0;i<allparents.length;i++){
if(curSelPartition != allparents[i].num){
$("#"+allparents[i].num).removeClass("drag-active-div");
}
}
})
});
mydrag();
});
var every_x = [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null];
var every_y = [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null];
var status1=true,status2=true,status3=true,status4=true;
function removeByValue(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == val) {
arr.splice(i, 1);
break;
}
}
}
Array.prototype.insert = function (index, item) {
this.splice(index, 1, item);
};
var mydrag = function () {
// target elements with the "draggable" class
interact('.draggable')
.draggable({
// enable inertial throwing
inertia: false,
// keep the element within the area of it's parent
restrict: {
restriction: ".right",
endOnly: true,
elementRect: {top: 0, left: 0, bottom: 1, right: 1}
},
maxPerElement: 100,
onmove: dragMoveListener,
//首次推拽坐标记录;
onstart: function (event) {
var s = $(event.target);
var target = event.target,
x = (Math.floor(parseFloat(target.getAttribute('data-x'))) || 0),
y = (Math.floor(parseFloat(target.getAttribute('data-y'))) || 0),
xr=(Math.floor(parseFloat(target.getAttribute('data-x'))) || 0)+Math.floor(parseFloat(s.width())),
yb=(Math.floor((parseFloat(target.getAttribute('data-y')))) || 0)+Math.floor(parseFloat(s.height()));
//获取被拖拽元素拖拽前的初始位置
curX = x;
curY = y;
curW = xr - x;
curH = yb - y;
partitionNumber = target.id;
console.log(event)
},
//拖拽结束后回调(缩放不回调此方法)
onend: function (event) {
var textEl = event.target.querySelector('p.lt');
var s = $(event.target);
var curParent = createPrent(event.target.id);
curParent.x = (Math.floor(parseFloat(event.target.getAttribute('data-x'))) || 0);
curParent.y = (Math.floor(parseFloat(event.target.getAttribute('data-y'))) || 0);
curParent.w = Math.floor(parseFloat(s.width()));
curParent.h = Math.floor(parseFloat(s.height()));
//拖拽碰撞检测如果发现重叠则回退到拖拽前位置
if (!checkConflict(curParent)) {
event.target.style.width = curW + 'px';
event.target.style.height = curH + 'px';
event.target.style.webkitTransform = event.target.style.transform =
'translate(' + curX + 'px,' + curY + 'px)';
event.target.setAttribute('data-x', curX);
event.target.setAttribute('data-y', curY);
event.target.querySelector('p.wh').textContent ='W:'+(curW) + 'px / H:' +(curH) + 'px';
var textEl = event.target.querySelector('p.lt');
textEl && (textEl.textContent =position(event));
drag_s(event);
//记录当前坐标信息
curParent.w = curW;
curParent.h = curH;
curParent.x = curX;
curParent.y = curY;
//更新分区维护集合
updParent(curParent);
return;
}
textEl && (textEl.textContent = position(event));
drag_s(event);
updParent(curParent);
}
})
.resizable({
// resize from all edges and corners
edges: {left: true, right: true, bottom: true, top: true},
// keep the edges inside the parent
restrictEdges: {
outer: '.right',
endOnly: true
},
// minimum size
restrictSize: {
min: {width: 50, height: 50}
},
inertia: false
})
//缩放监听方法
.on('resizemove', function (event) {
var s = $(event.target);
var target = event.target,
x = (Math.floor(parseFloat(target.getAttribute('data-x'))) || 0),
y = (Math.floor(parseFloat(target.getAttribute('data-y'))) || 0),
xr=(Math.floor(parseFloat(target.getAttribute('data-x'))) || 0)+Math.floor(parseFloat(s.width())),
yb=(Math.floor(parseFloat(target.getAttribute('data-y'))) || 0)+Math.floor(parseFloat(s.height()));
// update the element's style
// translate when resizing from top or left edges
x += Math.floor(event.deltaRect.left);
y += Math.floor(event.deltaRect.top);
xr += Math.floor(event.deltaRect.right);
yb += Math.floor(event.deltaRect.bottom);
var s = $(event.target);
var sid = s.attr('id');
var ns = parseInt(sid);
var i = ns * 2
every_y[i] = null;
every_x[i] = null;
every_y[i + 1] = null;
every_x[i + 1] = null;
var curParent = createPrent(event.target.id);
curParent.w = xr - x;
curParent.h = yb - y;
curParent.x = x;
curParent.y = y;
if (!checkConflict(curParent)) {
for (var i = 0; i < allparents.length; i++) {
if(event.target.id == allparents[i].num){
if (allparents[i].x != "" && allparents[i].x != null) {
curX = allparents[i].x;
}
if (allparents[i].y != "" && allparents[i].y != null) {
curY = allparents[i].y;
}
if (allparents[i].w != "" && allparents[i].w != null) {
curW = allparents[i].w;
}
if (allparents[i].h != "" && allparents[i].h != null) {
curH = allparents[i].h;
}
}
}
target.style.width = curW + 'px';
target.style.height = curH + 'px';
target.style.webkitTransform = target.style.transform =
'translate(' + curX + 'px,' + curY + 'px)';
target.setAttribute('data-x', curX);
target.setAttribute('data-y', curY);
target.querySelector('p.wh').textContent ='W:'+(curW) + 'px / H:' +(curH) + 'px';
var textEl = event.target.querySelector('p.lt');
textEl && (textEl.textContent =
position(event));
//数据更新
curParent.w = curW;
curParent.h = curH;
curParent.x = curX;
curParent.y = curY;
updParent(curParent);
return;
}
//更新当前拖拽元素坐标信息
console.log(curParent);
console.log(allparents);
updParent(curParent);
target.style.width = (xr-x) + 'px';
target.style.height =(yb-y) + 'px';
target.style.webkitTransform = target.style.transform =
'translate(' + x + 'px,' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
target.querySelector('p.wh').textContent ='W:'+(xr-x) + 'px / H:' +(yb-y) + 'px';
var textEl = event.target.querySelector('p.lt');
textEl && (textEl.textContent =
position(event));
})
.on('resizeend', function (event) {
drag_s(event)
});
//拖拽监听方法
function dragMoveListener(event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (Math.floor(parseFloat(target.getAttribute('data-x'))) || 0) + Math.floor(parseFloat(event.dx)),
y = (Math.floor(parseFloat(target.getAttribute('data-y'))) || 0) + Math.floor(parseFloat(event.dy));
var s = $(event.target)
var sid = s.attr('id');
var ns = parseInt(sid);
var i = ns * 2
every_y[i] = null;
every_x[i] = null;
every_y[i + 1] = null;
every_x[i + 1] = null;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
function position(e) {
return 'X:' + e.target.getAttribute('data-x') + "px / Y:" + e.target.getAttribute('data-y') + 'px'
}
function myclose(event) {
$(event.target).find(".close").click(function () {
$('.left').find('#' + $(this).parent().attr('id')).css('display', 'block');
$(this).parent().remove();
removeParent(event.target.id);
drag_c(event)
})
}
function drag_s(event) {
var s = $(event.target)
var sid = s.attr('id');
var ns = parseInt(sid);
var i = ns * 2
// top right bottom left //data-y data-x+width data-y+height data-x
every_y.insert(i, parseFloat(s.attr('data-y')));
every_x.insert(i, parseFloat(s.attr('data-x')) + parseFloat(s.width()));
every_y.insert(i + 1, parseFloat(s.attr('data-y')) + parseFloat(s.height()));
every_x.insert(i + 1, parseFloat(s.attr('data-x')));
//console.log(every_x, every_y)
}
function drag_c(event) {
var s = $(event.target)
var sid = s.attr('id');
var ns = parseInt(sid);
var i = ns * 2
every_y.insert(i, 0);
every_x.insert(i, 0);
every_y.insert(i + 1, 0);
every_x.insert(i + 1, 0);
};
window.dragMoveListener = dragMoveListener;
};
})()
/***
* 碰撞检测方法
* @param event 当前检测分区
*/
function checkConflict(event) {
var size = allparents.length;
if (allparents.length == 0)
return true;
else if (allparents.length > 1) {
let doing = true;
let start = 0;
let next = 0;
//当前操作分区信息
let startObj = event;
//被比较分区信息
let nextObj;
do {
size--;
nextObj = allparents[start];
if (nextObj == null) {
start++;
continue;
}
//当前分区为其自己本身跳过进入下次循环
if (nextObj.num == startObj.num) {
start++;
continue;
}
//比较节点
let x = startObj.x;
let y = startObj.y;
let w = startObj.w;
let h = startObj.h;
if (nextObj != null) {
//被比较节点
let nx = nextObj.x;
let ny = nextObj.y;
let nw = nextObj.w;
let nh = nextObj.h;
if ((x >= nx && y >= ny && nx + nw > x && ny + nh > y) ||
x <= nx && y < ny && x + w > nx && y + h > ny) {//左上角与右下角重叠
doing = false;
return doing;
} else if ((x <= nx && y >= ny && x + w > nx && y < ny + nh) ||
(x > nx && y < ny && x < nx + nw && y + h > ny)) { // 左下角与右上角重叠
doing = false;
return doing;
} else if ((x <= nx && y < ny && x + w > nx && y + h > ny) ||
(x >= nx && y > ny && x < nx + nw && y < ny + nh)) { // 下边与上边重叠
doing = false;
return doing;
} else if ((x >= nx && y <= ny && x < nx + nw && ny < y + h) ||
(x <= nx && y >= ny && nx < x + w && y < ny + nh)) { // 下边与上边重叠(宽度不一样)
doing = false;
return doing;
} else if ((x >= nx && y >= ny && x < nx + nw && y < ny + nh) ||
(x <= nx && y > ny && nx < x + w && y < ny + nh)) { // 左边与右边重叠
doing = false;
return doing;
} else if ((x >= nx && y <= ny && x < nx + nw && ny < y + h) ||
(x <= nx && y >= ny && nx < x + w && y < ny + nh)) { // 左边与右边重叠(高度不一样)
doing = false;
return doing;
}
start++;
}
} while (size >= 0);
return true;
}
return true;
}
//删除分区
function removeParent(id) {
var copy = new Array();
for (var i = 0; i < allparents.length; i++) {
if (id != allparents[i].num) {
copy.push(allparents[i])
}
}
allparents = copy;
}
//更新分区
function updParent(cur) {
for (var i = 0; i < allparents.length; i++) {
if (cur.num == allparents[i].num) {
if (cur.x != "" && cur.x != null) {
allparents[i].x = cur.x;
}
if (cur.y != "" && cur.y != null) {
allparents[i].y = cur.y;
}
if (cur.w != "" && cur.w != null) {
allparents[i].w = cur.w;
}
if (cur.h != "" && cur.h != null) {
allparents[i].h = cur.h;
}
}
}
}
调通后效果截图如下
大家搞完可能和我的样式不太一样我这个是在拟态框上的,使用的是bootstrap的样式库,样式可以根据自己的项目自行调整,就这么多吧 不扯废话了。
本文地址:https://blog.csdn.net/qq_41132489/article/details/110204828