首頁  >  文章  >  web前端  >  javascript實作PC網頁裡的拖曳效果_javascript技巧

javascript實作PC網頁裡的拖曳效果_javascript技巧

WBOY
WBOY原創
2016-05-16 15:10:551482瀏覽

幾年前,我參與設計開發一個房產網的項目,我負責前端工作,由於專案經理要求比較高,參考了很多房產類網站比較優秀的功能,想把別人比較優秀的設計和想法集合到一起,那時的設計稿和功能實現,簡直就是改了又改,今天做好的一個很好的效果,可能第二天就要推到重來,算了,不說這些了,還是說說我們今天要講解的案例吧,不知道大家訪問過搜房網沒有(完全沒有做廣告之嫌,搜房網,可以給點廣告費不),其中有一個功能產品經理特別喜歡,那,就是下面的這個:

這是現在的效果,可能改了一些,原來的效果是,裡面的這張圖是可以上下左右拖動的,然後房子上面的顯示的樓棟號,也跟著圖片一起移動,當時js能力還不行,未能實現專案經理的要求,不過後來專案經理又把這個效果推掉了,換了另外的一個效果

儘管專案經理不想要這個效果了,但是當時就在我心裡留下了一個節,到今天都忘不了這個梗。

好了,這就是我今天想寫這篇部落格的初衷,希望能給想實現這類拖曳效果,但是不知道該怎麼去實現的同學,提供一種思路,不給青春留遺憾,當然實作拖曳的方法很多,這裡就只介紹JavaScript中的一種方法,慢慢體會一下其中的原理!

好了,梗也說完了,開始正題,我們先要明白,拖曳到底是一個什麼東西,你也知道,我也知道,但是我還是想來描述一下:

拖曳就是一個容器,你用滑鼠可以在頁面上拖著到處跑,廢話,精確的描述應該是,滑鼠移到容器上,然後滑鼠按下去,注意要按著不放,然後拖曳滑鼠,容器能跟著滑鼠跑,放開滑鼠,容器就停在那裡不動了,現實中的例子就是桌子上有一個盒子,我用手放在盒子上,然後移動盒子,手停盒子停,手拿開,盒子不動了,嘻嘻,都懂了哈!

別以為上面說了一堆的廢話,我們可以從中得到很多的信息,總結如下就是:

拖曳 = 滑鼠按下 + 滑鼠移動 + 老鼠彈上

這樣就完成了一個拖曳任務,好了,原來這就是拖曳的原理,想實現拖曳,自然實現上面的3個動作,便可以模擬拖曳效果,好,對應JavaScript中的語法就是需要實現這3個動作:

onmousedown , onmousemove , onmouseup

 實現的程式碼就應該是:

obj.onmousedown = function(ev){
   obj.onmousemove = function(ev){
 
   } ;
   obj.onmouseup = function(ev){
   
   };
   
}

為什麼後面2個動作要寫的裡面,好好回味一下,好了,第一步的大概思路就有了,下一步就需要考慮怎麼讓物體跟著滑鼠一起移動,思路大概是這樣的:

首先物體是需要決定定位的,因為我們需要操作它的left和top值,才能讓它移動,然後就是要考慮滑鼠了,滑鼠位移,本身就會有一個距離,如果我們知道滑鼠移動了多遠,然後把這個距離給物體,那物體是不是也和滑鼠一樣,移動了相同的距離,這不就實現拖曳了嗎?哈哈,思路一點點有,感覺萌萌噠~ 現在的問題就是怎麼獲取滑鼠的距離,如果需要深入了解,請複習一下盒子模型,這裡我就不說了,很多大神也有相關的博客,我用一張圖表示一下:

說明:藍色框為螢幕寬高,黑色粗框為瀏覽器可視區寬高(瀏覽器縮小效果),黑色細框為滑鼠要拖曳的對象,如圖可知,取得滑鼠的座標,可以用event.clientX,event.clientY來獲取,喔了;

計算的大致原理可以參考下圖:

說明:左邊為初始位置,右邊為目標位置,原點為滑鼠位置,大黑框為瀏覽器可視寬度,小黑框為拖曳對象,看拖曳對像到目標位置的狀態,取得滑鼠的最終位置,再減去滑鼠距離物件的差值,再賦值給物件的top,left值,也可以取得滑鼠的位置差值,再用初始的top,left值加上差值,我們採用第一種,第二種也可以,自己去試試看:

 obj.onmousedown = function(ev){
  var ev = ev || event;
  var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
 
  document.onmousemove = function(ev){
    var ev = ev || event;
    obj.style.left = ev.clientX - disX + 'px';
    obj.style.top = ev.clientY - disY + 'px';
  };
  document.onmouseup = function(ev){
    var ev = ev || event;
    document.onmousemove = document.onmouseup = null;
  };
}

这里说明一下:onmousemove和onmouseup之所以用document对象而不用obj对象,是因为如果用obj对象,鼠标在obj内部还好,如果在obj外面的话,拖拽会很怪异,你也可以改成obj体会一下,最后我们在鼠标弹起的时候将事件都清空;

上面的基本拖拽就算完成了,但是细心的同学一定会问,如果页面上有文字的话,拖拽物体会将文字选中,这效果岂不是怪怪的,没错,这是因为拖拽的时候触发了浏览器的默认选择事件,所以,在拖拽的时候,我们要清除这个默认事件,那怎么清除呢?

下面给一个兼容性写法:

if(ev.stopPropagation){
   ev.stopPropagation();
}else{
  ev.cancelBubble = true; //兼容IE
}
//简写成
ev.stopPropagation ? ev.stopPropagation() : ev.cancelBubble = true;

将上面的代码放在onmousedown下,鼠标按下就清除浏览器默认事件,文字就不会被选中了,好了,一个简单的拖拽效果就完成了,当然你现在是看不到效果,之所以不给demo链接是为了让你自己试着写一写,这样印象更深刻,

好了,那问题又来了,到这里就这样完了吗?。。。。。。按本人的风格,当然没有,干货还在后面!

如果我想实现这样一个效果,就是这一个大的容器里面(可以是box,也可以是document),怎么样能让我们的拖拽对象不跑出去呢,换句话说,拖到边缘就拖不动了,耶,是不是很多人想要实现的效果,哈哈,我们看看实现的原理是什么:

现实生活中,一个物体在一个盒子里跑不出去,是因为有堵墙,那我们只要能模拟出这堵墙,就可以把物体框起来,那这堵墙要怎么做呢?我们可以换个思路,当拖拽对象拖到边缘的时候,比如说拖到右边,我们将它的left固定住,是不是就不能再往右了,因为left值不能再加了,那么拖到底部,同理我们将top值固定住,就不能再往下拖了,理解吗?

最终的结果就是如下:

//左侧
if(obj.offsetLeft <=0){
  obj.style.left = 0;
};
//右侧
if(obj.offsetLeft >= pWidth - oWidth){
  obj.style.left = pWidth - oWidth + 'px'; 
};
//上面
if(obj.offsetTop <= 0){
  obj.style.top = 0; 
};
//下面
if(obj.offsetTop >= pHeight - oHeight){
  obj.style.top = pHeight - oHeight + 'px'; 
};

说明:pWidth,pHeight 表示父级元素的宽高(这里是表示相对于父级的宽高限制),oWidth,oHeigt表示拖拽元素的宽高

最后,我将整个拖拽代码整理了一下:

/*
      参数说明:
      元素绝对定位,父级相对定位,如果父级为window,则可以不用
      传一个参数,表示父级为window,物体相对于window范围拖动
      传2个参数,则父级为第二个参数,物体相对于父级范围拖动
      参数为id值
    */
    function drag(obj,parentNode){
      var obj = document.getElementById(obj);
      if(arguments.length == 1){
        var parentNode = window.self; 
        var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight;  
      }else{
        var parentNode = document.getElementById(parentNode);
        var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight;
      }
      obj.onmousedown = function(ev){
        var ev = ev || event;
        var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
        var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight;
         
        //阻止冒泡时间
        ev.stopPropagation &#63; ev.stopPropagation() : ev.cancelBubble = true;
       
         
        document.onmousemove = function(ev){
          var ev = ev || event;
          obj.style.left = ev.clientX - disX + 'px';
          obj.style.top = ev.clientY - disY + 'px';
           
          //左侧
          if(obj.offsetLeft <=0){
            obj.style.left = 0;
          };
          //右侧
          if(obj.offsetLeft >= pWidth - oWidth){
            obj.style.left = pWidth - oWidth + 'px'; 
          };
          //上面
          if(obj.offsetTop <= 0){
            obj.style.top = 0; 
          };
          //下面
          if(obj.offsetTop >= pHeight - oHeight){
            obj.style.top = pHeight - oHeight + 'px'; 
          };
        };
        document.onmouseup = function(ev){
          var ev = ev || event;
          document.onmousemove = document.onmouseup = null;
        };
      }
         
    }

说明:我这里处理的效果是,如果传一个参数,表示相对的对象是window对象,如果传2个参数,第一个是拖拽对象,第二个为相对父级

开篇就说了,搜房网的那个图片拖拽效果是我的一个心结,我写了一个类似的效果,供大家参考,因为自己没有买服务器,所以效果我就不展示了,直接把代码贴出来,供大家参考:

css:

<style>
.box{
  width:600px;
  height:400px;
  margin:50px auto;
  position:relative;
  overflow:hidden;
}
#box{
  width:1000px;
  height:800px;
  position:absolute;
  left:50%;
  top:50%;
  margin:-400px 0 0 -500px;
}
#pic{ width:800px; height:600px; background:url(images/pic1.jpg) no-repeat; position:absolute; left:100px; top:100px; }
#pic:hover{
  cursor:move;
}
</style>

html:

<div class="box">
    <div id="box">
      <div id="pic"></div>
    </div>
  </div>

javascript:

window.onload = function(){
     
    drag("pic","box");
    function drag(obj,parentNode){
      var obj = document.getElementById(obj);
      if(arguments.length == 1){
        var parentNode = window.self; 
        var pWidth = parentNode.innerWidth,pHeight = parentNode.innerHeight;  
      }else{
        var parentNode = document.getElementById(parentNode);
        var pWidth = parentNode.offsetWidth,pHeight = parentNode.offsetHeight;
      }
      obj.onmousedown = function(ev){
        var ev = ev || event;
        var disX = ev.clientX - this.offsetLeft,disY = ev.clientY - this.offsetTop;
        var oWidth = obj.offsetWidth,oHeight = obj.offsetHeight;
         
        //阻止冒泡时间
        ev.stopPropagation &#63; ev.stopPropagation() : ev.cancelBubble = true;
       
         
        document.onmousemove = function(ev){
          var ev = ev || event;
          obj.style.left = ev.clientX - disX + 'px';
          obj.style.top = ev.clientY - disY + 'px';
           
          //左侧
          if(obj.offsetLeft <=0){
            obj.style.left = 0;
          };
          //右侧
          if(obj.offsetLeft >= pWidth - oWidth){
            obj.style.left = pWidth - oWidth + 'px'; 
          };
          //上面
          if(obj.offsetTop <= 0){
            obj.style.top = 0; 
          };
          //下面
          if(obj.offsetTop >= pHeight - oHeight){
            obj.style.top = pHeight - oHeight + 'px'; 
          };
        };
        document.onmouseup = function(ev){
          var ev = ev || event;
          document.onmousemove = document.onmouseup = null;
        };
      }
         
    }
     
     
  }

效果完全是用的那个封装代码块,引用起来也挺方便,有人会问了,你这用的id获取DOM元素,一个页面只能用一次啊,如果页面多次使用呢,有道理,解决方案之一,那就命名不同的id呗,又不犯法,方案二,获取id的地方改成获取class,但是要注意的是,getElementsByClassName是获取的class集合,需要改写一下,这里我就不写了,有兴趣的同学自行改写一下,好了,到这里真的结束了!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn