先附上一张效果图,里面的图片是我从虽虽酱的微博上扒下来。刚好做九宫格效果图,记录下来也是为了让自己能理解的更深刻,不至于写完过了一阵子又忘,代码什么的都可以写,最主要是的逻辑思想!!(也就是说为什么要这样做)~
说一下其中的逻辑
实现效果
首先明确一下这个拖拽照片墙的效果:
1.当我们按下鼠标,开始进行拖动
2.鼠标移动,需要拖动的图片跟着一起移动
3.鼠标松开,被选中的图片与拖拽的图片交换位置,停止移动。
对应三个鼠标事件,onmousedown
鼠标按下、onmousemove
鼠标移动、onmouseup
鼠标按键松开。
简单看一下布局
// html
- ![](images/1.jpg)
- ![](images/2.jpg)
- ![](images/3.jpg)
- ![](images/4.jpg)
- ![](images/5.jpg)
- ![](images/6.jpg)
- ![](images/7.jpg)
- ![](images/8.jpg)
- ![](images/9.jpg)
// css
* {
margin:0;
padding:0;
}
#photo {
position: relative;
width: 660px;
margin: 50px auto;
}
li {
width: 200px;
height: 200px;
float: left;
list-style: none;
margin:10px;
}
li img {
width: 200px;
height: 200px;
}
操作步骤
1.这里的图片在li
里面,都是float
布局。所以得将浮动布局修改为定位布局。(改为定位布局是方便记录每一张图片相对于浏览器顶部和左边的偏移量,也就是offsetLeft
和offsetTop
值)
var oUl = document.getElementsByTagName('ul')[0];
var aLi = oUl.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
aLi[i].style.position = 'absolute';
aLi[i].style.left = arr[i][0] + 'px';
aLi[i].style.top = arr[i][1] + 'px';
aLi[i].style.margin = '0px';
}
2.获取每个li的偏移量(后面要根据偏移量来计算具体位置)
var arr = [];
for( var i = 0; i < aLi.length; i++) {
// 获取每个li的偏移量
arr.push([aLi[i].offsetLeft, aLi[i].offsetTop]);
}
3.给每个li标签绑定拖拽功能
for (var i = 0; i < aLi.length; i++) {
aLi[i].index = i;
darg(aLi[i]); // darg是封装的拖拽函数
}
重点函数
前面给每个li标签绑定拖拽功能的时候,出现了一个darg函数,接下来就是写这个darg拖拽函数。
// 拖拽函数
function darg(obj) {
// 鼠标按下去
obj.onmousedown = function (event) {
var event = event || window.event;
// 获取当前图片的位置
var ax = obj.offsetLeft;
var ay = obj.offsetTop;
obj.style.zIndex = z++;
// 获取按下去时鼠标的坐标值
var x = event.clientX;
var y = event.clientY;
// 鼠标移动让图片跟着动,获取鼠标移动的位置,将移动的距离赋值给当前的图片的left和top,这样看起来像是图片在移动
document.onmousemove = function () {
var event = event || window.event;
var l = event.clientX - x;
var t = event.clientY - y;
obj.style.left = l + ax + 'px';
obj.style.top = t + ay + 'px';
// 碰撞成功的时候将被碰撞的li添加边框线
// 其他的边框线要去掉,碰撞成功的距离obj最近的li添加边框线
var li = near(obj);
for(var i =0; i
这中间有一个near
函数,这个函数是找到碰撞检测成功的距离拖拽元素最近的li
。
// 找到碰撞检测成功的距离拖拽元素最近的li
function near(obj) {
var n = 100000;
var index = 0;
// 先获取所有的碰撞成功的li
for (var i =0; i
在near
函数中有个碰撞检测的函数collision
,还有一个勾股定理计算距离的函数。
碰撞检测有这几种情况不会被碰撞到,
// 碰撞检测
function collision(obj1, obj2) {
var L1 = obj1.offsetLeft;
var R1 = L1 + obj1.offsetWidth;
var T1 = obj1.offsetTop;
var B1 = T1 + obj1.offsetHeight;
var L2 = obj2.offsetLeft;
var R2 = L2 + obj2.offsetWidth;
var T2 = obj2.offsetTop;
var B2 = T2 + obj2.offsetHeight;
if (R2 < L1 || B2 < T1 || T2 > B1 || L2 > R1) { // 未碰撞成功的几种情况
return false;
} else {
return true;
}
}
// 勾股定理计算距离
function distance(obj1, obj2) {
var a = obj1.offsetLeft - obj2.offsetLeft;
var b = obj1.offsetTop - obj2.offsetTop;
return Math.sqrt(a*a + b*b);
}
运动框架
在交换位置的时候有个缓冲运动效果,类似于手风琴效果。也就是startMove
函数。这个函数也是之前封装的框架,主要逻辑是距离与速度成正比,而且不能出现小数,要用到Math.ceil
和Math.floor
分别向上向下取整。
缓冲运动一定要记得取整,不然会出现到不了目的地(也就是离目的地有距离),
通过定时器setInterval
还有clearInterval
来完成。
//startMove(oDiv, {width: 400, height: 400}, function); //想要调用的时候是这个样子的。
// function startMove(obj, attr, iTarget, fnEnd) //只能传一个样式,一个值。
function startMove(obj, json, fnEnd)
{
clearInterval(obj.timer);
obj.timer=setInterval(function (){
var bStop = true; //假设:假设所有的值都已经到了
for (var attr in json) {
var cur = 0;
if (attr === 'opacity') {
cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
}
else {
cur = parseInt(getStyle(obj, attr));
}
var speed = (json[attr] - cur) / 6;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if( cur != json[attr]) { //有一个值不等于目标点,说明假设不成立
bStop = false;
}
if (attr === 'opacity') {
obj.style.filter = 'alpha(opacity:' + (cur + speed) + ')';
obj.style.opacity = (cur + speed) / 100;
}
else {
obj.style[attr] = cur + speed + 'px';
}
}
if(bStop) { //说明中间没有出现没到的情况, 意思是值都到了
clearInterval(obj.timer); //关闭定时器
fnEnd(); //执行回调函数
}
}, 30);
}