先附上一張效果圖碳却,里面的圖片是我從雖雖醬的微博上扒下來。剛好做九宮格效果圖庄涡,記錄下來也是為了讓自己能理解的更深刻,不至于寫完過了一陣子又忘搬设,代碼什么的都可以寫穴店,最主要是的邏輯思想!D醚ā(也就是說為什么要這樣做)~
說一下其中的邏輯
實現(xiàn)效果
首先明確一下這個拖拽照片墻的效果:
1.當我們按下鼠標泣洞,開始進行拖動
2.鼠標移動,需要拖動的圖片跟著一起移動
3.鼠標松開默色,被選中的圖片與拖拽的圖片交換位置球凰,停止移動。
對應三個鼠標事件腿宰,onmousedown
鼠標按下呕诉、onmousemove
鼠標移動、onmouseup
鼠標按鍵松開吃度。
簡單看一下布局
// html
<div id="photo">
<ul>
<li>![](images/1.jpg)</li>
<li>![](images/2.jpg)</li>
<li>![](images/3.jpg)</li>
<li>![](images/4.jpg)</li>
<li>![](images/5.jpg)</li>
<li>![](images/6.jpg)</li>
<li>![](images/7.jpg)</li>
<li>![](images/8.jpg)</li>
<li>![](images/9.jpg)</li>
</ul>
</div>
// 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的偏移量(后面要根據(jù)偏移量來計算具體位置)
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是封裝的拖拽函數(shù)
}
重點函數(shù)
前面給每個li標簽綁定拖拽功能的時候,出現(xiàn)了一個darg函數(shù)拖刃,接下來就是寫這個darg拖拽函數(shù)删壮。
// 拖拽函數(shù)
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';
// 碰撞成功的時候?qū)⒈慌鲎驳膌i添加邊框線
// 其他的邊框線要去掉央碟,碰撞成功的距離obj最近的li添加邊框線
var li = near(obj);
for(var i =0; i<aLi.length; i++) {
aLi[i].style.border = 'none';
}
if(li) {
li.style.border = '2px solid red';
}
};
// 鼠標抬起解綁鼠標事件,實現(xiàn)交換功能
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
// 實現(xiàn)交換功能
var li = near(obj);
var temp = 0; // 臨時變量當做中間變量來實現(xiàn)索引值的交換
if(li) {
li.style.border = '2px solid red';
// 碰撞成功的li實現(xiàn)移動到拖拽元素的位置
startMove(li, {left:arr[obj.index][0], top:arr[obj.index][1]});
// 拖拽元素實現(xiàn)移動到碰撞成功的位置上
startMove(obj, {left:arr[li.index][0], top:arr[li.index][1]});
li.style.border = '';
//索引值交換
temp = obj.index;
obj.index = li.index;
li.index = temp;
li.style.border = '';
} else { //沒有碰撞移回自己的位置
startMove(obj, {left:arr[obj.index][0], top:arr[obj.index][1]});
}
};
// 阻止默認拖拽事件
return false;
}
}
這中間有一個near
函數(shù)均函,這個函數(shù)是找到碰撞檢測成功的距離拖拽元素最近的li
亿虽。
// 找到碰撞檢測成功的距離拖拽元素最近的li
function near(obj) {
var n = 100000;
var index = 0;
// 先獲取所有的碰撞成功的li
for (var i =0; i<aLi.length; i++) {
if(collision(obj, aLi[i]) && obj != aLi[i]) {
// 檢測到碰撞成功的元素比較
// 獲取碰撞成功的元素與拖拽元素obj的距離
var c = distance(obj, aLi[i]);
if(c < n) {
n = c;
index = i; // 把判斷出距離最近的li的索引保存下來
}
}
}
if(index != 0){
return aLi[index];
}else{
return false;
}
}
在near
函數(shù)中有個碰撞檢測的函數(shù)collision
,還有一個勾股定理計算距離的函數(shù)苞也。
碰撞檢測有這幾種情況不會被碰撞到洛勉,
// 碰撞檢測
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
函數(shù)收毫。這個函數(shù)也是之前封裝的框架攻走,主要邏輯是距離與速度成正比,而且不能出現(xiàn)小數(shù)此再,要用到Math.ceil
和Math.floor
分別向上向下取整昔搂。
緩沖運動一定要記得取整,不然會出現(xiàn)到不了目的地(也就是離目的地有距離),
通過定時器setInterval
還有clearInterval
來完成输拇。
//startMove(oDiv, {width: 400, height: 400}, function); //想要調(diào)用的時候是這個樣子的摘符。
// function startMove(obj, attr, iTarget, fnEnd) //只能傳一個樣式,一個值策吠。
function startMove(obj, json, fnEnd)
{
clearInterval(obj.timer);
obj.timer=setInterval(function (){
var bStop = true; //假設:假設所有的值都已經(jīng)到了
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) { //說明中間沒有出現(xiàn)沒到的情況, 意思是值都到了
clearInterval(obj.timer); //關閉定時器
fnEnd(); //執(zhí)行回調(diào)函數(shù)
}
}, 30);
}