`
kalllx
  • 浏览: 62979 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

draggable.js源码分析

阅读更多
/**
 * 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;
		//拖动后的left=原先的left+拖动位移
		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;
		}
		//如果拖动对象有父对象,对于计算出来的left和top要加上父对象的left和top
		if (e.data.parnet != document.body) {
			if ($.boxModel == true) {//只针对盒子模型进行设置
				left += $(e.data.parent).scrollLeft();
				top += $(e.data.parent).scrollTop();
			}
		}
		
		if (opts.axis == 'h') {//如果是水平拖动 则只改left
			dragData.left = left;
		} else if (opts.axis == 'v') {//如果是垂直,则只改top
			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;//获取拖动对象的proxy对象,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
		});
	}
	//3按下鼠标时候执行的方法
	function doDown(e){
		var opts = $.data(e.data.target, 'draggable').options;//获取拖动对象中option配置信息
		//拖和放是对应的,现在来设置放对象
		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; //只要有一个匹配就ok了
			} else {
				return true; //如果目标地,没设置接受对象,则所有对象都可以被拖进来。
			}
		});
		$.data(e.data.target, 'draggable').droppables = droppables;
		
		var proxy = $.data(e.data.target, 'draggable').proxy;//获取拖动对象的proxy对象,proxy就是我们在拖动时候随鼠标移动的对象
		if (!proxy){//如果没有专门配置一个proxy
			if (opts.proxy){//看看option中是否有proxy,如果有
				if (opts.proxy == 'clone'){//proxy为'clone'
					proxy = $(e.data.target).clone().insertAfter(e.data.target);//复制当前dom,并查到当前dom后面
				} else {
					proxy = opts.proxy.call(e.data.target, e.data.target);//如果为一个函数,则调用这个函数返回值
				}
				$.data(e.data.target, 'draggable').proxy = proxy;//同事把opts.里面的proxy协会到拖动对象的draggable缓存配置里面去。
			} else {//如果哪里都没有的话
				proxy = $(e.data.target);//proxy对象就是本身了
			}
		}
		
		proxy.css('position', 'absolute');//因为是拖动,所以要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]);//触发目标对象的over事件
			} 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 {//如果没有reverte设置,直接修改拖动dom的位置即可。
			$(e.data.target).css({
				position:'absolute',
				left:e.data.left,
				top:e.data.top
			});
			removeProxy();//移除proxy对象
			checkDrop();
		}
		
		
		
		opts.onStopDrag.call(e.data.target, e);
		
		function removeProxy(){
			if (proxy){
				proxy.remove();
			}
			$.data(e.data.target, 'draggable').proxy = null;
		}
		//该方法检验是否将对象拖动到目标对象内,如果拖动进去了,触发目标对象的_drop事件,并设置进入标志。
		function checkDrop(){
			var dropped = false;
			//对drop对象进行遍历操作
			$.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){//如果revert是true,就会还原
						$(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');//接触document上的事件,否则移动结束后,元素还是会变位置的
		return false;
	}
	//1,注册draggable方法到$()上
	$.fn.draggable = function(options){
		if (typeof options == 'string'){//option为字符串的时候,进行处理
			switch(options){
			case 'options':
				return $.data(this[0], 'draggable').options;//为option,实际是获取,该对象缓存数据中名为draggable中的options对象,可以参考jquery $data()方法
			case 'proxy':
				return $.data(this[0], 'draggable').proxy;//获取缓存中的proxy
			case 'enable'://执行禁止拖动操作。
				return this.each(function(){
					$(this).draggable({disabled:false});
				});
			case 'disable'://允许拖动
				return this.each(function(){
					$(this).draggable({disabled:true});
				});
			}
		}
		
		return this.each(function(){	//通过调用$().draggable()方法来实现拖动
//			$(this).css('position','absolute');
			
			var opts;
			var state = $.data(this, 'draggable');//获取对象上在缓存中存放的draggable对象,首次肯定为空
			if (state) {
				state.handle.unbind('.draggable');
				opts = $.extend(state.options, options);
			} else {
				opts = $.extend({}, $.fn.draggable.defaults, options || {});//合并配置参数,来源为默认配置+传递进来的参数
			}
			
			if (opts.disabled == true) {  //如果参数中含有禁止拖动的配置,则返回
				$(this).css('cursor', 'default');
				return;
			}
			
			var handle = null;    //handle是实际触发拖动的对象
            if (typeof opts.handle == 'undefined' || opts.handle == null){
                handle = $(this);  //handle为空,则默认为调用对象数组中本身的子元素
            } else {
                handle = (typeof opts.handle == 'string' ? $(opts.handle, this) : handle);//如果为字符串,包装成本身jquery对象和本身两个对象。
            }
			$.data(this, 'draggable', {  //把opts信息更新到子元素的缓存中的draggable对象中
				options: opts,
				handle: handle
			});
			
			// bind mouse event using event namespace draggable
			handle.bind('mousedown.draggable', {target:this}, onMouseDown); //注册拖动对象的mousedown事件,后面的.draggable是为事件注册一个命名空间,好处是unbind(.draggable)=unbind(.mousedown)+unbind(.mouseover)
			handle.bind('mousemove.draggable', {target:this}, onMouseMove);//注册鼠标滑动事件  //注意this代表的是$()元素中的每一个子dom元素
			
			function onMouseDown(e) {
				if (checkArea(e) == false) return;  

				var position = $(e.data.target).position();//获取被拖动元素的top 和left
				var data = {  //封装data 对象,记录width,height,pageX,pageY,left 和top是为了方便计算拖动后的位置
					startPosition: $(e.data.target).css('position'), //absolute  or relative or active
					startLeft: position.left, 
					startTop: position.top, 
					left: position.left,
					top: position.top,
					startX: e.pageX, //鼠标点击时的x坐标
					startY: e.pageY, //鼠标点击时的y坐标
					target: e.data.target, //子dom
					parent: $(e.data.target).parent()[0] //拖动对象的父dom
				};
				
				$(document).bind('mousedown.draggable', data, doDown); //在document上绑定三个鼠标事件。根据事件的下传特性,当在目标对象上执行drap.就会触发document上的drap.
				$(document).bind('mousemove.draggable', data, doMove); //以下主要分析doDown,doMove 和doUp的实现过程
				$(document).bind('mouseup.draggable', data, doUp);
			}
			
			function onMouseMove(e) {  //move事件主要是当鼠标移动到可以拖拽元素上,鼠标形状进行改变
				if (checkArea(e)){
					$(this).css('cursor', opts.cursor);
				} else {
					$(this).css('cursor', 'default');
				}
			}
			
			// check if the handle can be dragged  判断鼠标拖动点,离handle的四面八方是否小于设置的adge,如果有一边小于则不拖动
			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 = {  //2默认配置信息
			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,//鼠标点击拖动对象时候,proxy的位移,称作德尔塔
			deltaY:null,
			handle: null,
			disabled: false,
			edge:0,
			axis:null,	// v or h  水平还是垂直方向拖动
			
			onStartDrag: function(e){},
			onDrag: function(e){},
			onStopDrag: function(e){}
	};
})(jQuery);
分享到:
评论
1 楼 xiyan 2016-04-25  
很不错。嘿嘿

相关推荐

    jquery拖曳显示或隐藏图像插件coveringBad.js

    五、实战示例与源码分析 在实践中,可以访问提供的演示地址(http://www.jq22.com/jquery-info478)查看coveringBad.js的实际效果。通过阅读源码,我们可以深入了解插件的工作原理,学习如何结合jQuery事件和CSS3...

    Vue 拖拽组件源码

    源码分析: Vue.Draggable 的核心在于其对原生浏览器的 drag 和 drop 事件的封装。通过监听这些事件,它实现了元素的拖放、移动、复制和取消等操作。在源码中,你会看到以下几个关键部分: 1. **Data management**...

    jquery-easyui-1.2.5源码

    总结起来,jQuery EasyUI 1.2.5的源码是一本生动的JavaScript和前端框架实践教程,它揭示了组件化、模块化开发的理念,同时也为我们展示了如何通过JavaScript优雅地封装和复用代码。通过学习和理解这些源码,开发者...

    jquery api, jquery ui api, jquery源码分析

    **jQuery API** jQuery 是一款广泛使用的 JavaScript 库,它的出现极大地简化了网页的 ...通过学习jQuery API、jQuery UI API以及源码分析,开发者不仅可以提升开发效率,还能编写出更高效、更优雅的JavaScript代码。

    jQuery.UI.1.8.The.User.Interface.Library + 源码

    源码分析可以帮助我们理解每个组件的实现方式,以及如何通过JavaScript和CSS实现这些交互效果。通过学习jQuery UI,开发者可以快速构建出功能强大的交互界面,同时减少编写重复代码的时间,提升开发效率。 在...

    jquery ui 1.8.2 code

    `js` 文件夹存放 JavaScript 文件,其中 `ui/jquery.ui.core.js` 是核心模块,其他如 `ui/jquery.ui.widget.js`、`ui/jquery.ui.mouse.js` 等则为各个组件的具体实现;`css` 文件夹包含样式表,用于定义组件的视觉...

    iview.test测试demo包含拖拽排序 mock测试 iview模板

    3. **JavaScript源码文件**:Vue.js的主入口文件(如main.js),配置文件(如vue.config.js),以及可能的业务逻辑和路由配置等。 4. **Mock配置文件**:可能包含Mock.js的配置文件,定义了模拟的API接口及其返回...

    jquery-ui-1.8.2.custom.zip

    总结来说,"jquery-ui-1.8.2.custom.zip"提供了完整的jQuery UI 1.8.2版本,包含了必要的HTML示例、CSS样式和JavaScript源码,便于我们快速集成和自定义。通过深入研究这个压缩包,开发者不仅可以掌握jQuery UI的...

    JavaScript模态对话框类(拖拽时动画)

    JavaScript模态对话框是一种在网页中常用的设计元素,它用于向用户显示临时信息或需要用户交互的任务,如确认操作、输入数据等。本教程将深入探讨如何创建一个具有拖拽功能和动画效果的模态对话框,这在现代网页开发...

    JS实现的表单简单拖拽特效源码.zip

    本资源“JS实现的表单简单拖拽特效源码.zip”提供了一种使用JavaScript实现的表单元素的拖拽功能,这在现代Web界面设计中是一个常见的需求,例如用于自定义布局或者构建拖放式交互应用。 拖拽特效的实现主要依赖于...

    JAVA+VUE可拖拽BI可视化系统源码

    - VUE.js是一个轻量级的JavaScript前端框架,用于构建用户界面。在这个项目中,VUE.js负责将后端提供的数据进行展示和交互,实现拖拽功能可能使用了Dragula或Vue.Draggable等库。此外,VUE的组件化特性使得代码可...

    JavaScript可拖动DIV

    5. **源码分析**:提供的文件"JavaScript可拖动DIV.html"应该包含了完整的实现示例。通过阅读和理解源码,你可以更深入地了解这个功能的工作原理,并根据实际需求进行修改和扩展。 总之,实现JavaScript可拖动div...

    HelloDrag-源码.rar

    【标题】"HelloDrag-源码.rar"是一个压缩文件,其中包含了名为"HelloDrag-源码.zip"的源代码项目。这个项目很可能是一个教学或示例项目...通过分析和运行源码,开发者可以深入理解拖放机制,并将其应用到自己的项目中。

    REACT-DRAGNDROP:REACT-DRAGNDROP-HOOKS

    1. `src/Draggable.js`: 这个文件很可能实现了`useDraggable` Hook,其中包含了拖放元素的状态管理以及拖放事件的处理逻辑。 2. `src/Droppable.js`: 另一个可能的文件,用于实现`useDroppable` Hook,处理目标区域...

    jquery半透明拖拽窗口插件

    8. `js`目录 - 通常包含插件的JavaScript源码,以及可能依赖的其他脚本文件。主要的插件文件可能会命名为`jquery.draggable.window.js`或类似名称,它实现了拖动、最大化和最小化等功能。 为了在自己的项目中使用这...

    easy 拖拽

    标签“源码”和“工具”提示我们,这里可能涉及到具体的编程代码和实用工具。我们将深入探讨拖放功能在Web开发中的应用,以及如何使用源代码实现这一功能。 拖放(Drag and Drop)是用户界面设计中的一种常见交互...

    jQuery实现支持拖拽对话框插件源码.zip

    5. **源码分析**: - 查看`132687012312399678`文件,可能包含HTML、CSS和JavaScript文件,它们共同构成了对话框插件。 - HTML文件中,对话框可能作为一个元素存在,通过CSS进行样式设置。 - JavaScript文件中,...

    jquery ui

    源码分析** 源码通常由JavaScript和CSS组成,开发者可以通过阅读源码理解jQuery UI的内部工作方式,学习如何实现类似的交互效果。同时,源码也是学习JavaScript和前端开发的宝贵资源。 **6. 集成与优化** 为了...

    react版拖拉拽表单

    通过分析和学习这个项目的源码,你可以深入理解React中的拖拉拽实现,以及如何构建可自定义的表单组件。 总结起来,"react版拖拉拽表单"项目是一个利用React.js构建的交互式表单,它利用React的事件系统、数据管理...

Global site tag (gtag.js) - Google Analytics