文章出處

前面的話

  從本文開始,介紹javascript動畫系列。javascript本身是具有原生拖放功能的,但是由于兼容性問題,以及功能實現的方式,用的不是很廣泛。javascript動畫廣泛使用的還是模擬拖拽。本文將詳細介紹該內容

 

原理介紹

  模擬拖拽最終效果和在桌面上移動文件夾的效果類似

  鼠標按下時,拖拽開始。鼠標移動時,被拖拽元素跟著鼠標一起移動。鼠標抬起時,拖拽結束

  所以,拖拽的重點是確定被拖拽元素是如何移動的

  假設,鼠標按下時,鼠標對象的clientX和clientY分別為x1和y1。元素距離視口左上角x軸和y軸分別為x0和y0

  鼠標移動的某一時刻,clientX和clientY分別為x2和y2

  所以,元素移動的x軸和y軸距離分別為x2-x1和y2-y1

  元素移動后,元素距離視口左上角x軸和y軸的位置分別為

    X = x0 + (x2-x1)
    Y = y0 + (y2-y1)

 

代碼實現

  將上面的原理用代碼實現如下

  鼠標按下時,初始態的x0和y0分別用offsetLeft和offsetTop表示

  鼠標移動時,瞬時態的x和y分別賦值為定位后元素的left和top

<div id="test" style="height:100px;width:100px;background:pink;position:absolute;top:0;left:0;"></div>
<script>
test.onmousedown = function(e){
    e = e || event;
    //獲取元素距離定位父級的x軸及y軸距離
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    var x1 = e.clientX;
    var y1 = e.clientY;

    test.onmousemove = function(e){
        e = e || event;
        //獲取此時鼠標距離視口左上角的x軸及y軸距離
        x2 = e.clientX;
        y2 = e.clientY;    
        //計算此時元素應該距離視口左上角的x軸及y軸距離
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //將X和Y的值賦給left和top,使元素移動到相應位置
        test.style.left = X + 'px';
        test.style.top = Y + 'px';
    }

    test.onmouseup = function(e){
        //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可
        test.onmousemove = null;
    }
}
</script>

【另一種實現】

  由于使用上面的DOM0級事件處理程序時,將只能綁定一個函數,將不利于擴展。所以,將其改寫為DOM2級事件處理程序IE事件處理程序的兼容寫法

<div id="test" style="height:100px;width:100px;background:pink;position:absolute;top:0;left:0;"></div>
<script>
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}
(function(){
    var x0,y0,x1,y1,isMoving;
    var ele = document.getElementById('test');

var mousedownHandler = function(e){
    e = e || event;
    //獲取元素距離定位父級的x軸及y軸距離
    x0 = this.offsetLeft;
    y0 = this.offsetTop;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    x1 = e.clientX;
    y1 = e.clientY;
    //按下鼠標時,表示正在運動
    isMoving = true;
}
var mousemoveHandler = function(e){
    //如果沒有觸發down事件,而直接觸發move事件,則函數直接返回
    if(!isMoving){
        return;
    }
    e = e || event;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    var x2 = e.clientX;
    var y2 = e.clientY;    
    //計算此時元素應該距離視口左上角的x軸及y軸距離
    var X = x0 + (x2 - x1);
    var Y = y0 + (y2 - y1);
    //將X和Y的值賦給left和top,使元素移動到相應位置
    ele.style.left = X + 'px';
    ele.style.top = Y + 'px';
}
var mouseupHandler = function(e){
    //鼠標抬起時,表示停止運動
    isMoving = false;
}

addEvent(ele,'mousedown',mousedownHandler);
addEvent(ele,'mousemove',mousemoveHandler)
addEvent(ele,'mouseup',mouseupHandler)

})();
</script>

代碼優化

  使用上面的代碼時,會出現一個問題。當鼠標拖動的太快,比mousemove事件的觸發間隔還要快時,鼠標就會從元素上離開。這樣就停止了元素的拖拽過程

  此時,如果把mousemove和mouseup事件都加在document上時,即可解決

<div id="test" style="height:100px;width:100px;background:pink;position:absolute;top:0;left:0;"></div>
<script>
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}
(function(){
    var x0,y0,x1,y1,isMoving;
    var ele = document.getElementById('test');

var mousedownHandler = function(e){
    e = e || event;
    //獲取元素距離定位父級的x軸及y軸距離
    x0 = this.offsetLeft;
    y0 = this.offsetTop;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    x1 = e.clientX;
    y1 = e.clientY;
    //按下鼠標時,表示正在運動
    isMoving = true;
}
var mousemoveHandler = function(e){
    //如果沒有觸發down事件,而直接觸發move事件,則函數直接返回
    if(!isMoving){
        return;
    }
    e = e || event;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    var x2 = e.clientX;
    var y2 = e.clientY;    
    //計算此時元素應該距離視口左上角的x軸及y軸距離
    var X = x0 + (x2 - x1);
    var Y = y0 + (y2 - y1);
    //將X和Y的值賦給left和top,使元素移動到相應位置
    ele.style.left = X + 'px';
    ele.style.top = Y + 'px';
}
var mouseupHandler = function(e){
    //鼠標抬起時,表示停止運動
    isMoving = false;
}

addEvent(ele,'mousedown',mousedownHandler);
addEvent(ele,'mousemove',mousemoveHandler)
addEvent(ele,'mouseup',mouseupHandler)

})();
</script>

拖拽沖突

  由于文字和圖片默認支持原生拖放,如果將原生拖放和模擬拖拽摻雜在一起,將造成與預想效果不符的情況

  如果拖放的元素內容存在文字,且文字被選中會觸發文字的原生拖放效果

  在文字上面雙擊鼠標,即可選中文字,再移動鼠標時,會觸發文字的原生拖放效果,如下所示

  只要在mousedown事件阻止瀏覽器的默認行為即可

<div id="test" style="height:100px;width:100px;background:pink;position:absolute;top:0;left:0;">測試文字</div>
<script>
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}
(function(){
    var x0,y0,x1,y1,isMoving;
    var ele = document.getElementById('test');

var mousedownHandler = function(e){
    e = e || event;
    //獲取元素距離定位父級的x軸及y軸距離
    x0 = this.offsetLeft;
    y0 = this.offsetTop;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    x1 = e.clientX;
    y1 = e.clientY;
    //按下鼠標時,表示正在運動
    isMoving = true;
}
var mousemoveHandler = function(e){
    //如果沒有觸發down事件,而直接觸發move事件,則函數直接返回
    if(!isMoving){
        return;
    }
    e = e || event;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    var x2 = e.clientX;
    var y2 = e.clientY;    
    //計算此時元素應該距離視口左上角的x軸及y軸距離
    var X = x0 + (x2 - x1);
    var Y = y0 + (y2 - y1);
    //將X和Y的值賦給left和top,使元素移動到相應位置
    ele.style.left = X + 'px';
    ele.style.top = Y + 'px';
}
var mouseupHandler = function(e){
    //鼠標抬起時,表示停止運動
    isMoving = false;
}
var preventDefaultHandler = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
}
addEvent(ele,'mousedown',mousedownHandler);
addEvent(ele,'mousedown',preventDefaultHandler);
addEvent(document,'mousemove',mousemoveHandler)
addEvent(document,'mouseup',mouseupHandler)

})();
</script>

IE兼容

  以上代碼在IE8-瀏覽器中仍然無法阻止默認行為。此時,為了實現IE兼容,需要使用全局捕獲setCapture()和釋放捕獲releaseCapture()

  首先,先看一下全局捕獲的效果

  下面代碼中,開啟全局捕獲之后,頁面中的所有點擊效果,都相當于針對按鈕一的點擊效果。釋放捕獲后,效果消失

  [注意]IE瀏覽器完全支持全局捕獲;chrome不支持,使用全局捕獲會報錯;firefox不報錯,但靜默失敗

<button id="btn1">按鈕一</button>
<button id="btn2">開啟按鈕一的全局捕獲</button>
<script>
btn1.onclick = function(){
    alert(1);
}
btn2.onclick = function(){
    if(btn1.setCapture){
        if(btn2.innerHTML.charAt(0) == ''){
            btn1.setCapture();
            btn2.innerHTML = '關閉按鈕一的全局捕獲';
        }else{
            btn1.releaseCapture();
            btn2.innerHTML = '開啟按鈕一的全局捕獲';    
        }
    }
}
</script>

  通過在IE瀏覽器設置全局捕獲來達到取消文字原生拖放的默認行為

<div id="test" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">測試文字</div>
<script>
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}
(function(){
    var x0,y0,x1,y1,isMoving;
    var ele = document.getElementById('test');

var mousedownHandler = function(e){
    e = e || event;
    //獲取元素距離定位父級的x軸及y軸距離
    x0 = this.offsetLeft;
    y0 = this.offsetTop;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    x1 = e.clientX;
    y1 = e.clientY;
    //按下鼠標時,表示正在運動
    isMoving = true;
}
var mousemoveHandler = function(e){
    //如果沒有觸發down事件,而直接觸發move事件,則函數直接返回
    if(!isMoving){
        return;
    }
    e = e || event;
    //獲取此時鼠標距離視口左上角的x軸及y軸距離
    var x2 = e.clientX;
    var y2 = e.clientY;    
    //計算此時元素應該距離視口左上角的x軸及y軸距離
    var X = x0 + (x2 - x1);
    var Y = y0 + (y2 - y1);
    //將X和Y的值賦給left和top,使元素移動到相應位置
    ele.style.left = X + 'px';
    ele.style.top = Y + 'px';
}
var mouseupHandler = function(e){
    //鼠標抬起時,表示停止運動
    isMoving = false;
    //釋放全局捕獲
    if(ele.releaseCapture){
        ele.releaseCapture();
    }    
}
var preventDefaultHandler = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    //IE8-瀏覽器阻止默認行為
    if(ele.setCapture){
        ele.setCapture();
    }
}
addEvent(ele,'mousedown',mousedownHandler);
addEvent(ele,'mousedown',preventDefaultHandler);
addEvent(document,'mousemove',mousemoveHandler)
addEvent(document,'mouseup',mouseupHandler)

})();
</script>

源碼查看


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()