原生JS實現(xiàn)拖拽照片墻效果

先附上一張效果圖碳却,里面的圖片是我從雖雖醬的微博上扒下來。剛好做九宮格效果圖庄涡,記錄下來也是為了讓自己能理解的更深刻,不至于寫完過了一陣子又忘搬设,代碼什么的都可以寫穴店,最主要是的邏輯思想!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布局。所以得將浮動布局修改為定位布局规肴。(改為定位布局是方便記錄每一張圖片相對于瀏覽器頂部和左邊的偏移量捶闸,也就是offsetLeftoffsetTop值)

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.ceilMath.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);
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奴曙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌草讶,老刑警劉巖洽糟,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異堕战,居然都是意外死亡坤溃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門嘱丢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薪介,“玉大人,你說我怎么就攤上這事越驻≈” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵缀旁,是天一觀的道長记劈。 經(jīng)常有香客問我,道長并巍,這世上最難降的妖魔是什么目木? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮懊渡,結(jié)果婚禮上刽射,老公的妹妹穿的比我還像新娘。我一直安慰自己剃执,他們只是感情好誓禁,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肾档,像睡著了一般现横。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天戒祠,我揣著相機與錄音骇两,去河邊找鬼。 笑死姜盈,一個胖子當著我的面吹牛低千,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馏颂,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼示血,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了救拉?” 一聲冷哼從身側(cè)響起难审,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎亿絮,沒想到半個月后告喊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡派昧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年黔姜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒂萎。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡秆吵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出五慈,到底是詐尸還是另有隱情纳寂,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布泻拦,位于F島的核電站烈疚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏聪轿。R本人自食惡果不足惜爷肝,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陆错。 院中可真熱鬧灯抛,春花似錦、人聲如沸音瓷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绳慎。三九已至纵竖,卻和暖如春漠烧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背靡砌。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工已脓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人通殃。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓度液,卻偏偏與公主長得像,于是被迫代替她去往敵國和親画舌。 傳聞我的和親對象是個殘疾皇子堕担,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容