效果图
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
ul {
width: 480px;
height: 640px;
margin: 10px auto;
background: yellow;
padding: 10px;
position: relative;
list-style: none;
}
ul li {
width: 150px;
height: 150px;
margin: 5px;
float: left;
background: lightgreen;
cursor: move;
/*position: relative;*/
/*相对父级;通过getComputedStyle获取的是相对于自身的位置的left和top;*/
}
ul li img {
width: 100%;
height: 100%;
user-select: none;
pointer-events: none;
}
</style>
</head>
<body>
<ul>
<li><img src="../day02/images/images/23.jpg"></li>
<li><img src="../day02/images/images/22.jpg"></li>
<li><img src="../day02/images/images/21.jpg"></li>
<li><img src="../day02/images/images/20.jpg"></li>
<li><img src="../day02/images/images/19.jpg"></li>
<li><img src="../day02/images/images/18.jpg"></li>
<li><img src="../day02/images/images/17.jpg"></li>
<li><img src="../day02/images/images/16.jpg"></li>
<li><img src="../day02/images/images/15.jpg"></li>
<li><img src="../day02/images/images/14.jpg"></li>
<li><img src="../day02/images/images/13.jpg"></li>
<li><img src="../day02/images/images/12.jpg"></li>
</ul>
<script src="event.js"></script>
<script>
let oLis = document.getElementsByTagName("li");
// 循环每一个li;让其有拖拽效果;
// 通过倒序,先给最后一张加position,那么前面几张位置是不受影响的;
// offsetLeft : 获取是当前盒子的外边框到父级参照物的内边框的距离,包含margin值;这样会导致设置的left值偏大
for (let i = oLis.length - 1; i >= 0; i--) {
oLis[i].style.left = oLis[i].offsetLeft - 5 + "px";
oLis[i].style.top = oLis[i].offsetTop - 5 + "px";
oLis[i].style.position = "absolute"; //
// 第一个迅速脱离文档流;第二个元素会跑到第一个位置上;
new Drag(oLis[i]).on("selfdown", changeIndex).on("selfmove", isHit).on("selfup", changePosi);
}
let zIndex = 0;
// 增加拖拽元素的索引;
function changeIndex() {
// 保证当前拖拽的元素z-index永远是最大的;
this.ele.style.zIndex = ++zIndex;
}
// 碰撞检测
function isHit() {
// this--> Drag的实例;上的ele属性就是被拖拽的元素;
let curLi = this.ele;
this.hit = [];
for (let i = 0; i < oLis.length; i++) {
if (curLi === oLis[i]) continue; // 和自己没必要进行检测;
if (curLi.offsetLeft + curLi.offsetWidth < oLis[i].offsetLeft || curLi.offsetTop + curLi.offsetHeight <
oLis[i].offsetTop || curLi.offsetTop > oLis[i].offsetTop + oLis[i].offsetHeight || curLi
.offsetLeft > oLis[i].offsetLeft + oLis[i].offsetWidth) {
// 只要满足其中一个条件,那么说明这个拖拽的盒子和li没有重合的部分;没有碰撞;
// 说明没碰到
oLis[i].style.background = "";
} else {
oLis[i].style.background = "pink";
this.hit.push(oLis[i]); // 把有交集的元素放入hit这个数组中;
}
}
}
function changePosi() {
//this
let ary = this.hit;
let curLi = this.ele;
if (ary) {
for (let i = 0; i < ary.length; i++) {
let cur = ary[i];
// 计算出左上角点之间的距离的平方;
cur.distance = Math.pow(cur.offsetLeft - curLi.offsetLeft, 2) + Math.pow(cur.offsetTop - curLi
.offsetTop, 2);
cur.style.background = "";
}
};
// 对数组的distance进行排序;找出最小值;
ary.sort(function (a, b) {
return a.distance - b.distance;
})
// 实现位置的交换
if (ary[0]) {
this.ele.style.left = ary[0].style.left;
this.ele.style.top = ary[0].style.top;
ary[0].style.left = this.l + "px";
ary[0].style.top = this.t + "px";
} else {
// 如果没有任何碰撞的元素,直接设置原有的样式;
this.ele.style.left = this.l + "px";
this.ele.style.top = this.t + "px";
}
this.hit = []; // 清空hit;
}
</script>
</body>
</html>
event.js
function EventFire() {}
EventFire.prototype.on=function (type,fn) {
// 把方法订阅到这个EventFire这个类的实例上;
if(!this[type]){
// 第一次执行,给obj新增键值对,并且属性值是[];
this[type]=[];
}
let ary = this[type];
//遍历ary中的数组成员,判断是否绑定过;
for(let i=0;i<ary.length;i++){
if(ary[i]===fn){
return;
}
}
ary.push(fn);
// 为了实现链式调用on订阅的方法;
return this;
}
EventFire.prototype.emit=function (type) {
let a = this[type];
if(a){//如果是undefined,不进循环
for(let i=0;i<a.length;i++){
if(typeof a[i]==="function"){
a[i].call(this);
}
}
}
}
EventFire.prototype.off=function (type,fn) {
let ary = this[type];
for(let i=0;i<ary.length;i++){
if(fn===ary[i]){
//ary.splice(i,1);数组塌陷;
ary[i]=null;
return;
}
}
}
function Drag(ele) {
// this-->实例;Drag的一个实例
// 把元素放在Drag的实例上;
this.ele = ele;
this.ele.onmousedown=this.down.bind(this);
}
// 原型继承
Drag.prototype=new EventFire();
Drag.prototype.down=function (e) {
// console.log(this);// this--> Drag的实例
this.x=e.clientX;
this.y=e.clientY;
this.l = parseFloat(getComputedStyle(this.ele).left);
this.t = parseFloat(getComputedStyle(this.ele).top);
document.onmousemove=this.move.bind(this);
document.onmouseup = this.up.bind(this);
this.emit("selfdown");
}
Drag.prototype.move=function (e) {
let changeX = e.clientX-this.x+this.l;
let changeY = e.clientY-this.y+this.t;
this.ele.style.left = changeX+"px";
this.ele.style.top= changeY+"px";
this.emit("selfmove")
}
Drag.prototype.up = function () {
document.onmousemove=null;
document.onmouseup =null;
this.emit("selfup");
}