说实话,jQuery比原生的js好用多了,本来想用原生写的,也写出来的,只是,感觉不像插件,所以用jQuery实现了一版。
实现的功能:可以指定拖拽的边界,在拖拽过程中,可以触发几个自定义事件
先说明一下我写的插件的原则:
1.常量分离出来,放在$.zUI.插件中
2.插件的主体执行函数命名为$.zUI.插件.fn
3.销毁函数命名为$.zUI.插件.unfn
这些规范,主要是为了以后写其他插件时,放在一起,精简代码用的,以后可能还会增加其他规则,以写出一个骨架来。
拖拽的原理其实比较简单,就是在鼠标落下后,添加一个mousemove事件,让元素跟随鼠标移动,鼠标抬起后,移除刚才的事件。别看下面的代码那么多,其实多数是控制元素拖拽的范围的,把这些代码忽略后,其余的东西,很少。
主要的两段代码如下:
$.zUI = $.zUI || {} /* * draggable * 参数:obj{ * bOffsetParentBoundary:是否以定位父亲元素为边界, * oBoundary:指定元素left和top的边界值,形如{iMinLeft:...,iMaxLeft:...,iMinTop:...,iMaxTop:...},与上一个参数互斥 * fnComputePosition:扩展函数,返回形如{left:...,top:...}的对象 * } * 支持的自定义事件: * "draggable.start":drag起始,就是鼠标down后触发 * "draggable.move":drag过程中多次触发 * "draggable.stop":drag结束触发,就是鼠标up后触发 */ //draggable常量 $.zUI.draggable = { defaults :{ bOffsetParentBoundary:false,//是否以定位父亲元素为边界 oBoundary:null,//边界 fnComputePosition:null//计算位置的函数 }, sFlagName:"zUIdraggable", sEventName:"zUIdraggableEvent", sOptsName:"draggableOpts" } $.zUI.draggable.fn = function(ele,opts){ var jqEle = $(ele); jqEle.data($.zUI.draggable.sOptsName,$.extend({},$.zUI.draggable.defaults,opts)); //如果该参数已经执行过draggable了,直接返回,仅相当于修改了配置参数 if(jqEle.data($.zUI.draggable.sFlagName)){ return; } jqEle.data($.zUI.draggable.sFlagName,true); jqEle.data($.zUI.draggable.sEventName,{ mousedown:function(ev){ var opts = jqEle.data($.zUI.draggable.sOptsName); var jqThis = $(this); jqThis.trigger("draggable.start"); var iOffsetX = ev.pageX - this.offsetLeft; var iOffsetY = ev.pageY - this.offsetTop; function fnMouseMove (ev) { var oPos = {}; if(opts.fnComputePosition){ oPos = opts.fnComputePosition(ev,iOffsetX,iOffsetY); }else{ oPos.iLeft = ev.pageX - iOffsetX; oPos.iTop = ev.pageY - iOffsetY; } var oBoundary = opts.oBoundary; if(opts.bOffsetParentBoundary){//如果以offsetParent作为边界 var eParent = jqThis.offsetParent()[0]; oBoundary = {}; oBoundary.iMinLeft = 0; oBoundary.iMinTop = 0; oBoundary.iMaxLeft = eParent.clientWidth - jqThis.outerWidth(); oBoundary.iMaxTop = eParent.clientHeight - jqThis.outerHeight(); } if(oBoundary){//如果存在oBoundary,将oBoundary作为边界 oPos.iLeft = oPos.iLeft < oBoundary.iMinLeft ? oBoundary.iMinLeft : oPos.iLeft; oPos.iLeft = oPos.iLeft > oBoundary.iMaxLeft ? oBoundary.iMaxLeft : oPos.iLeft; oPos.iTop = oPos.iTop < oBoundary.iMinTop ? oBoundary.iMinTop : oPos.iTop; oPos.iTop = oPos.iTop > oBoundary.iMaxTop ? oBoundary.iMaxTop : oPos.iTop; } jqThis.css({left:oPos.iLeft,top:oPos.iTop}); ev.preventDefault(); jqThis.trigger("draggable.move"); } var oEvent = { mousemove:fnMouseMove, mouseup:function(){ $(document).off(oEvent); jqThis.trigger("draggable.stop"); } }; $(document).on(oEvent); }}); jqEle.on(jqEle.data($.zUI.draggable.sEventName)); } $.zUI.draggable.unfn = function(e){ var jqEle = $(ele); if(jqEle.data($.zUI.draggable.sFlagName)){ jqEle.off(jqEle.data($.zUI.draggable.sEventName)); jqEle.data($.zUI.draggable.sFlagName,false); } }
需要说明的有几点:
1.鼠标落下后,要记录鼠标相对元素的位置,mousemove的过程中,要把这段距离减去;
2.jQuery的data方法,这个方法非常方便,可以讲数据和对应的元素绑定,jq.data(key,value)就出存储,jq.data(key)就是读取,jq.data(obj)也是存储。
3.undraggable就是把事件函数去掉了
4.jQuery的on方法非常强大,添加后还可以使用trigger方法来触发,有兴趣的同学可以到官方看看API,on方法非常暴躁,这里的自定义函数,就是用这两个方法实现的。
5.这里是把方法放在了$函数上,最后要把这个方法放在$.fn上,如下:
$.each(["draggable"],function(i,widget){ unWidget = "un"+widget; var w = {}; w[widget] = function(args){ this.each(function(){ $.zUI[widget].fn(this,args); }); return this; }; w[unWidget] = function(){ this.each(function(){ $.zUI[unWidget].unfn(this); }); return this; } $.fn.extend(w); });
这里是不是有点乱,其实这么写主要是为了以后写方便;
each除了在jq对象上用之外,还可以使用$.each(Array,fnCallBack);之后添加新的插件后,按照我之前的标准写,只需要在第一个参数上添加其他字符串就可以了。
仔细看看,就是添加了两个方法:draggable和undraggable;这两函数都调用this.each方法,让dragable和undraggable可以再每个元素上都执行。
最后,用一个匿名函数自执行把他们都包起来,为了防止$符号被其他的插件使用,传一个jQuery过去:
(function($){ ....... })(jQuery);
到此为止,这个插件就写完啦。下面是demo的链接地址。