博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

(十) 模拟 HTML5 实现 DragDrop

Posted on 2011-01-24 17:21  xuld  阅读(1199)  评论(3编辑  收藏  举报

DragDrop 看似简单,实现起来却不大容易。 HTML5 已经提供了用于支持 DragDrop 的事件。使用这些事件可以方便实现 dragdrop。介于有些朋友对 HTML5 的 dragdrop 不熟,在这里先介绍下标准的 dragdrop 。

dragdrop 不是新物,早在 IE4 就有了。但那时只是 IE 的独家技能,所以标准浏览器不支持此事件,现在 HTML5 采用了 IE 的方法,故目前最新版浏览器都支持 DragDrop 。

DragDrop 是 Drag (拖动) Drop(放下) 两个操作。

为实现 Drag ,需要知道以下的事件:

  • dragstart - 拖动开始事件
  • drag - 拖动时,这个事件不断出现
  • dragend - 拖动结束事件

 

为实现 Drop,需要知道以下的事件:

  • dragenter - 拖到当前区事件
  • dragover - 拖到当前区时,不断出现
  • dragleave - 拖出事件
  • drop - 在当前区停下事件

 

假如拖动一个元素到另一个元素, 发生事件的顺序为:

 

dragstart - drag (不断) - [ dragenter - dragover/drag (交替) - dragleave/drop - ] dragend

拖动事件的参数 e, 有个成员 dataTransfer , 表示当前拖动的数据。

 

默认下, img 和 a 标签可拖到。如果需要其它节点也支持拖动,必须指明  draggable="true" 。

 

 

根据上文,你肯能会写这样的代码:

 

 

 

  <div id="et">拖动委托层</div>
  <div id="drag" draggable="true">
     被拖动
  </div>
  <div class="asd" id="zone">
     拖动区域
</div> <script> $('drag').on('dragstart', function(e){ console.log(e.type); }).on('drag', function(e){ console.log(e.type); }).on('dragend', function(e){ console.log(e.type); }); </script>

但这个代码只能在 Chrome 正常执行。

对于 IE, 则需要先选择字,然后拖。

对于 Safari, 需加如下代码:

 

    <style>
           [draggable = true] {
                -khtml-user-drag: element;
            }
    </style>

对于 Firefox, 除了加上面的代码,还需加

 

function dragstart(e){
   e.dataTransfer.setData('Text', this.id);  // 火狐必须加这句
}

目前,各浏览器对 DragDrop 的支持不同。老浏览器也不支持 DragDrop 。

 

所以,必须找一个和谐的办法。

 

我的目标是模拟浏览器的 dragdrop, 让它和浏览器原生的一样。

先调用一个函数:

$('d1').setDraggable(true); // 设置可拖动

然后这个对象便能拖动,如果还要设置事件,可以:

$('drag').on('dragstart', function(e){
          console.log(e.type);
     }).on('drag',  function(e){
          console.log(e.type);
     }).on('dragend',  function(e){
          console.log(e.type);
    });

对于目标元素,同样可设置事件,处理拖到当前元素时的处理

$('zone1').on('dragenter', function(e){                 trace("dragenter");                 return false;             }).on('dragover', function(e){                 trace("dragover");                 return false;             }).on('dragleave', function(e){                 trace("dragleave");                 return false;             }).on('drop', function(e){                 trace("drop");             });

下面介绍如何实现:

对于 Drag ,需要用 mouse 事件模拟。

先写3个函数:分别处理 mousedown,  mousemove,  mouseup , 这个相对简单,不多废话。

 

 

function mousedown(e ){
   
     // 左键才继续
     if(e.which != 1)
         return;

     // 初始化位置。

     // 暂时保存导致事件发生的目标。
     Drag.target = this;

     // 手动触发  dragstart
    this.trigger('dragstart', e);
    
    document.on('mouseup', mouseup).on('mousemove', mousemove);
}

function mouseup(e ){
   
   // 左键才继续
  if(e.which != 1)
      return;

    // 手动触发  dragend
    this.trigger('dragend', e);

   // 通知已发生 Drop 事件。
    Drag.onDrop(e);

    document.un('mousemove', mousemove).un('mouseup', mouseup);
}


function mousemove(e )

    // 手动触发  drag
    this.trigger('drag', e);

     // 移动目标。
}


然后是实现 Drop,一个 element 拖动时如何正确判断当前到上面元素,并触发那个元素的 drop 事件?

方案甲:

  判断目标区域 Zone 是否发生 mouseenter 事件, 在 mouseenter 事件处理是否在拖动,如果同时发生 mouseenter 和 drag ,则 发生 dragenter 。

 

方案乙:

  在拖动时不断计算目标位置的坐标 (Bound) 和 当前元素,如果进入则发生 dragenter 。

  因此,先写一个函数判断是否在目标内:

 

function isEnter(bound, box){
     return ((bound.right < box.right && bound.right > box.left) || (bound.left < box.right && bound.left > box.left)) &&
					  ((bound.bottom < box.bottom && bound.bottom > box.top) || (bound.top < box.bottom && bound.top > box.top)) ;
}


然后在 drag 中,检查当前的 bound 和目标是否符合要求,如果符合要求,继续判断:

 

function raiseEvent(e){
  if(isEnter(bound, box)){

   // 如果上次的区域和当前区域一样,表示在同区发生拖动, 应发生 dragover
    if( Drap.zone == zone)  {
        zone.trigger('dragover', e);
   } else {

       // 如果上次在一个zone,现在没有,表示离开一个 zone
      if(Drap.zone) Drap.zone.trigger('dragleave', e);

      Drap.zone = zone;  // 保存当前的 zone

     zone.trigger('dragenter', e);
     return;

  } 

}

   // 现在没有激活的区,但上次有,说明已经拖到上次的外面
  if(Drap.zone){
     Drap.zone.trigger('dragleave', e);
     Drap.zone = null;
    }

}

这样稍微加工,就可以完成 DragDrop 。

 

 

 

 

具体源码在以后一起发。

XULD QQ 744257564