easyui之draggable控件分析,待完善

本文介绍了一个基于 jQuery EasyUI 的拖拽组件实现原理。该组件提供了丰富的配置选项,如设置代理元素、拖拽方向限制等,并支持与 droppable 组件交互,实现元素拖放功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * draggable - jQuery EasyUI
 * 
 * Licensed under the GPL:
 *   http://www.gnu.org/licenses/gpl.txt
 *
 * Copyright 2010 stworthy [ stworthy@gmail.com ] 
 */
(function($){
   function drag(e){
      var opts = $.data(e.data.target, 'draggable').options;
      
      var dragData = e.data;
      var left = dragData.startLeft + e.pageX - dragData.startX;
      var top = dragData.startTop + e.pageY - dragData.startY;
      
      if (opts.deltaX != null && opts.deltaX != undefined){
         left = e.pageX + opts.deltaX;
      }
      if (opts.deltaY != null && opts.deltaY != undefined){
         top = e.pageY + opts.deltaY;
      }
      
      if (e.data.parnet != document.body) {
         if ($.boxModel == true) {
            left += $(e.data.parent).scrollLeft();
            top += $(e.data.parent).scrollTop();
         }
      }
      
      if (opts.axis == 'h') {
         dragData.left = left;
      } else if (opts.axis == 'v') {
         dragData.top = top;
      } else {
         dragData.left = left;
         dragData.top = top;
      }
   }
   
   function applyDrag(e){
      var opts = $.data(e.data.target, 'draggable').options;
      var proxy = $.data(e.data.target, 'draggable').proxy;
      if (proxy){
         proxy.css('cursor', opts.cursor);
      } else {
         proxy = $(e.data.target);
         $.data(e.data.target, 'draggable').handle.css('cursor', opts.cursor);
      }
      proxy.css({
         left:e.data.left,
         top:e.data.top
      });
   }
   
   function doDown(e){
      var opts = $.data(e.data.target, 'draggable').options;
      
      var droppables = $('.droppable').filter(function(){
         return e.data.target != this;
      }).filter(function(){
         var accept = $.data(this, 'droppable').options.accept;
         if (accept){
            return $(accept).filter(function(){
               return this == e.data.target;
            }).length > 0;
         } else {
            return true;
         }
      });
      $.data(e.data.target, 'draggable').droppables = droppables;
      
      var proxy = $.data(e.data.target, 'draggable').proxy;
      if (!proxy){
         if (opts.proxy){
            if (opts.proxy == 'clone'){
               proxy = $(e.data.target).clone().insertAfter(e.data.target);
            } else {
               proxy = opts.proxy.call(e.data.target, e.data.target);
            }
            $.data(e.data.target, 'draggable').proxy = proxy;
         } else {
            proxy = $(e.data.target);
         }
      }
      
      proxy.css('position', 'absolute');
      drag(e);
      applyDrag(e);
      
      opts.onStartDrag.call(e.data.target, e);
      return false;
   }
   
   function doMove(e){
      
      drag(e);
      if ($.data(e.data.target, 'draggable').options.onDrag.call(e.data.target, e) != false){
         applyDrag(e);
      }
      
      var source = e.data.target;
      $.data(e.data.target, 'draggable').droppables.each(function(){
         var dropObj = $(this);
         var p2 = $(this).offset();
         if (e.pageX > p2.left && e.pageX < p2.left + dropObj.outerWidth()
               && e.pageY > p2.top && e.pageY < p2.top + dropObj.outerHeight()){
            if (!this.entered){
               $(this).trigger('_dragenter', [source]);
               this.entered = true;
            }
            $(this).trigger('_dragover', [source]);
         } else {
            if (this.entered){
               $(this).trigger('_dragleave', [source]);
               this.entered = false;
            }
         }
      });
      
      return false;
   }
   
   function doUp(e){
      drag(e);
      
      var proxy = $.data(e.data.target, 'draggable').proxy;
      var opts = $.data(e.data.target, 'draggable').options;
      if (opts.revert){
         if (checkDrop() == true){
            removeProxy();
            $(e.data.target).css({
               position:e.data.startPosition,
               left:e.data.startLeft,
               top:e.data.startTop
            });
         } else {
            if (proxy){
               proxy.animate({
                  left:e.data.startLeft,
                  top:e.data.startTop
               }, function(){
                  removeProxy();
               });
            } else {
               $(e.data.target).animate({
                  left:e.data.startLeft,
                  top:e.data.startTop
               }, function(){
                  $(e.data.target).css('position', e.data.startPosition);
               });
            }
         }
      } else {
         $(e.data.target).css({
            position:'absolute',
            left:e.data.left,
            top:e.data.top
         });
         removeProxy();
         checkDrop();
      }
      
      
      
      opts.onStopDrag.call(e.data.target, e);
      
      function removeProxy(){
         if (proxy){
            proxy.remove();
         }
         $.data(e.data.target, 'draggable').proxy = null;
      }
      
      function checkDrop(){
         var dropped = false;
         $.data(e.data.target, 'draggable').droppables.each(function(){
            var dropObj = $(this);
            var p2 = $(this).offset();
            if (e.pageX > p2.left && e.pageX < p2.left + dropObj.outerWidth()
                  && e.pageY > p2.top && e.pageY < p2.top + dropObj.outerHeight()){
               if (opts.revert){
                  $(e.data.target).css({
                     position:e.data.startPosition,
                     left:e.data.startLeft,
                     top:e.data.startTop
                  });
               }
               $(this).trigger('_drop', [e.data.target]);
               dropped = true;
               this.entered = false;
            }
         });
         return dropped;
      }
      
      $(document).unbind('.draggable');
      return false;
   }

   //构造函数
   $.fn.draggable = function(options){
      //如果传参是一个字符串,就根据字符串去执行命令
      if (typeof options == 'string'){
         switch(options){
            case 'options':
            //如果是'option',就取出来缓存到第一个元素上的draggable属性的对象的options属性对象
            return $.data(this[0], 'draggable').options;
            case 'proxy':
            //如果是'proxy',就取出来缓存到第一个元素上的draggable属性的对象的proxy属性对象
            return $.data(this[0], 'draggable').proxy;
            case 'enable':
            //如果是'enable',就针对每一个选中了的元素重新调用该方法,传参改为{disabled:false}
            return this.each(function(){
               $(this).draggable({disabled:false});
            });
         case 'disable':
            //如果是'enable',就针对每一个选中了的元素重新调用该方法,传参改为{disabled:true}
            //外面的this是一个jquery对象(类数组)
            return this.each(function(){
               //下面的thisdom元素对象
               $(this).draggable({disabled:true});
            });
         }
      }

      //对于每一个选中了的dom元素执行操作
      return this.each(function(){
//       $(this).css('position','absolute');
         
         var opts;
         //首先取出绑定到了dom元素draggable属性上的对象,注意这里是直接绑定到了dom元素上,而不是绑定jquery对象上,dom元素能保证两次拿到的是一个(document维护),但是jquery对象会每次使用选择器的时候都会重建一个,所以js脚本中一般缓存数据也是绑定到dom元素上的
         var state = $.data(this, 'draggable');
         if (state) {
            //如果dom元素上缓存了'draggable'属性,那么就首先给操作句柄解绑所有的拖拽事件
            state.handle.unbind('.draggable');
            //使用现在的参数对象去覆盖以前的参数
            opts = $.extend(state.options, options);
         } else {
            //如果dom元素上没有缓存'draggable'属性,说明是第一次开启拖拽功能,那么就直接用现在的参数去覆盖默认的参数
            opts = $.extend({}, $.fn.draggable.defaults, options || {});
         }
         
         if (opts.disabled == true) {
            //如果参数中有设置{disabled:true},那么就把光标恢复到默认,并直接返回
            $(this).css('cursor', 'default');
            return;
         }

         //拖拽对象的句柄,也是一个jquery对象
         var handle = null;
            if (typeof opts.handle == 'undefined' || opts.handle == null){
            //如果没有定义handle对象就使用dom元素自身
                handle = $(this);
            } else {
            //如果是个字符串,就当成选择器字符串在这个dom元素中查找
                handle = (typeof opts.handle == 'string' ? $(opts.handle, this) : handle);
            }
         //缓存参数对象模型和操作句柄对象
         $.data(this, 'draggable', {
            options: opts,
            handle: handle
         });

         //绑定鼠标按下和鼠标移动事件,都在命名空间draggable,传递的参数target是一个dom元素对象
         // bind mouse event using event namespace draggable
         handle.bind('mousedown.draggable', {target:this}, onMouseDown);
         handle.bind('mousemove.draggable', {target:this}, onMouseMove);
         
         function onMouseDown(e) {
            //这鼠标按下事件作为拖拽的触发事件
            if (checkArea(e) == false) return;

            var position = $(e.data.target).position();
            var data = {
               startPosition: $(e.data.target).css('position'),
               startLeft: position.left,
               startTop: position.top,
               left: position.left,
               top: position.top,
               startX: e.pageX,
               startY: e.pageY,
               target: e.data.target,//要被拖拽的dom元素对象
               parent: $(e.data.target).parent()[0]//target的父元素dom对象
            };

            //分别绑定鼠标按下事件、鼠标移动事件、鼠标松开事件
            $(document).bind('mousedown.draggable', data, doDown);
            $(document).bind('mousemove.draggable', data, doMove);
            $(document).bind('mouseup.draggable', data, doUp);
         }
         
         function onMouseMove(e) {
            //这个鼠标的移动事件作为基础事件,只负责处理光标显示,具体的拖动过程绑定的处理方法是在鼠标按下触发拖拽时临时绑定的
            if (checkArea(e)){
               $(this).css('cursor', opts.cursor);
            } else {
               $(this).css('cursor', 'default');
            }
         }
         
         // check if the handle can be dragged
         function checkArea(e) {
            var offset = $(handle).offset();
            var width = $(handle).outerWidth();
            var height = $(handle).outerHeight();
            var t = e.pageY - offset.top;
            var r = offset.left + width - e.pageX;
            var b = offset.top + height - e.pageY;
            var l = e.pageX - offset.left;
            
            return Math.min(t,r,b,l) > opts.edge;
         }
         
      });
   };
   
   $.fn.draggable.defaults = {
         proxy:null,    // 'clone' or a function that will create the proxy object, 
                  // the function has the source parameter that indicate the source object dragged.
         revert:false,
         cursor:'move',
         deltaX:null,
         deltaY:null,
         handle: null,
         disabled: false,
         edge:0,
         axis:null, // v or h
         
         onStartDrag: function(e){},
         onDrag: function(e){},
         onStopDrag: function(e){}
   };
})(jQuery);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值