Ext drag 那些事

//panel初始化拖拽函数
initDraggable : function() {
if (this.simpleDrag) {// default value is 'false'
this.initSimpleDraggable();
}
else {
this.dd = new Ext.panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
}

在EXT有一个Ext-dd-DragDropManager类(理解浏览器的事件传播模型很重要), 该类在初始化的第一件事就是在docment上注册
mousemove 如下:
Ext.define('Ext.dd.DragDropManager', {
singleton: true,
。。。。。。。
}, function() {
this._addListeners();
})

_addListeners: function() {
if ( document ) {
//初始化拖拽相关的事件(如下)
this._onLoad();
} else {
//确保document.body已加载完毕
if (this._timeoutCount <= 2000) {
setTimeout(this._addListeners, 10);
if (document && document.body) {
this._timeoutCount += 1;
}
}
}
},
_onLoad: function() {
this.init();
var Event = Ext.EventManager;
Event.on(document, "mouseup", this.handleMouseUp, this, true);
//当我们在document拖动鼠标时,会触发handleMouseMove函数,这里需要提醒的一点是: 在实际处理cmp的拖动
//事件时,是有限制的,例如拖动范围必须大于某个值(可佩的),才会触发, 具体的拖拽函数并不是实时执行的,而是利用
//setTimeOut(drageFn, 1000(可佩的))
Event.on(document, "mousemove", this.handleMouseMove, this, true);
Event.on(window, "unload", this._onUnload, this, true);
Event.on(window, "resize", this._onResize, this, true);
// Event.on(window, "mouseout", this._test);

}

Ext-dd-DragDropManager是一个单例模式,负责处理所有cmp控件的拖拽,Ext-dd-DragDropManager中有一个属性dragCurrent是用来
记录当前的拖拽对象,当退拽事件发生时,会通过dragCurrent.onDrag等类似的方式,来传递所有的与拖拽相关的事件处理机制

//接下来我们来具体看一下Ext.panel.Panel的拖拽初始化(从Ext.panel.DD说起)
1. Ext.panel.DD.constructor
var me = this;
me.panel = panel;
me.dragData = {panel: panel};
//下面这行代码的含义是: 建议打开ext文档的portal demo例子看一下,当拖拽时会出现两种现象,1.出现一个虚线框(样式
//是可以自定义的),2.当前拖动的panel好像没有了item,只是一个panel的框架, 当我们松开鼠标时,panel又恢复了。
//所以我要说的这两种现象的处理都在new Ext.panel.Proxy实现的,第一种现象是proxy(虚线框),第二种现象是ghost,panel的
//复制版本,他与当前的panel的关系是:
*constructor: function(panel, config){
* var me = this;
* me.panel = panel;
* me.id = me.panel.id +'-ddproxy';
* Ext.apply(me, config);
*}
*当我们开始拖拽的时候,他会调用Ext.panel.Proxy.show方法, 看一下show方法的源码,我们就明白了
me.panelProxy = new Ext.panel.Proxy(panel, cfg);
me.proxy = me.panelProxy.proxy;
me.callParent([panel.el, cfg]);
//还记得当拖拽panel的时候,我们的鼠标会变成"move"形状, 在Ext当中,当我们把鼠标放在某个cmp上时,如果出现move形状,
//那么这个cmp叫做"handlerEl", 知道这个东东,ok那我们就可以任意指定panel移动时的handlerEL了(扩展Ext.panel.DD),默认情况下是panel.header.el
//如果你的panel的header是false,那么此时的handler就是panel.body了, 没错,me.setupEl(panel)方法就是干这个事情(详细代码如下)
me.setupEl(panel);
2. Ext.panel.DD.setupEl
setupEl: function(panel){
var me = this,
header = panel.header,
//默认是panel.body
el = panel.body;

if (header) {//如果有header,那就是header.el
me.setHandleElId(header.id);
el = header.el;
}
if (el) {
//惊呆了,原来如此
el.setStyle('cursor', 'move');
me.scroll = false;
} else {
panel.on('boxready', me.setupEl, me, {single: true});
}
},

//关于该构造函数参数是什么意思,看一下他的子类Ext.panel.DD:
* me.callParent([panel.el, cfg]);
*
*
3.Ext.dd.DragSource.constructor(Ext.panel.DD的父类)
//el就是当前panel的el,说白了就是当前panel的dom
this.el = Ext.get(el);
//this.dragData, 该属性,在拖拽事件函数中可以拿到这个值
if(!this.dragData){
this.dragData = {};
}
Ext.apply(this, config);
if(!this.proxy){
//还记得上面提到proxy吗? 他就是那个虚线框,其实默认就是this.proxy
this.proxy = new Ext.dd.StatusProxy({
id: this.el.id + '-drag-status-proxy',
//指定是否在Repair的时候添加me.el.animate特效(更加线性)
animRepair: this.animRepair
});
}
//调用父类构造(在下面介绍)
this.callParent([this.el.dom, this.ddGroup || this.group,
{dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]);
//状态标识
this.dragging = false;

4.Ext.dd.DDProxy.constructor(Ext.dd.DragSource的父类)
if (id) {
//在父类Ext.dd.DragDrop(在下面介绍)
*在dd中group有何作用,我的理解是在同一group下的cmp target是支持互相drop的
*而这些group与cmp都是存储在Ext.dd.DragDropManager中的ids属性当中

}
this.init(id, sGroup, config);
//见5.xxxxx.createFrame
this.initFrame();

//在document.body的body.firstChild前面插入一个div元素
* <div id="${panel.el.id}-drag-status-proxy" style="position:absolute; visibility: hidden; cursor:move; border: 2px solid #aaa; zIndex: 999">
* </div>
*
5.Ext.dd.DDProxy.createFrame
var self = this,
body = document.body,
div,
s;

if (!body || !body.firstChild) {
setTimeout( function() { self.createFrame(); }, 50 );
return;
}

//Ext.dd.DragDrop.getDragEl 其实就是获取proxy.dom
div = this.getDragEl();

//div不为空,因为proxy是已经存在的
if (!div) {
div = document.createElement("div");
//就是this.el.id + '-drag-status-proxy'(还记得这样代码吗?)
div.id = this.dragElId;
s = div.style;

s.position = "absolute";
s.visibility = "hidden";
s.cursor = "move";
s.border = "2px solid #aaa";
s.zIndex = 999;
body.insertBefore(div, body.firstChild);
}

6.Ext.dd.DragDrop.init
//从方法名称,我可以得出target这个术语,每次在Ext的DD机制当中,target是其组成的一个部分,那么他的作用是什么的
//加入 我想将一个panel移动到另一panel当中。 首先目标panel必须的是一个target,当拖拽事件over在目标target上时,
//此时target就可以接受当前的proxy,
* 这id就是panel.el.dom(它不是proxy.dom哦)
*
this.initTarget(id, sGroup, config);
//监控dom of this.id的 mousedown事件(见下面handleMouseDown)
Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);

6.Ext.dd.DragDrop.initTarget
initTarget: function(id, sGroup, config) {
// configuration attributes
this.config = config || {};
//Ext.dd.DragDropManager这个东东,在开始的时候我有介绍它在EXT DD机制中所扮演的角色
this.DDMInstance = Ext.dd.DragDropManager;
this.groups = {};
if (typeof id !== "string") {
id = Ext.id(id);
}
this.id = id;
//还记得我之前说过的target吗? 他在dd中扮演的角色,以及角色是如何被创建的, ok addToGroup就是做这件事情的
//最终所有的target都会存放在Ext.dd.DragDropManager(我是单例的哦)的属性当中
this.addToGroup((sGroup) ? sGroup : "default");
//还记得我之前介绍的handler吗?
this.handleElId = id;
//设置一个默认的DragElId
this.setDragElId(id);
this.invalidHandleTypes = { A: "A" };
this.invalidHandleIds = {};
this.invalidHandleClasses = [];
//这个方法是在
this.applyConfig();
this.handleOnAvailable();
},
7.Ext.dd.DragDrop.applyConfig(Ext.dd.DDProxy已经复写)
applyConfig: function() {

// configurable properties:
// padding, isTarget, maintainOffset, primaryButtonOnly
this.padding = this.config.padding || [0, 0, 0, 0]; //padding
this.isTarget = (this.config.isTarget !== false); // is target
this.maintainOffset = (this.config.maintainOffset); //null
this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); //true

}
8.Ext.dd.DDProxy.applyConfig
this.callParent();
this.resizeFrame = (this.config.resizeFrame !== false); //false
this.centerFrame = (this.config.centerFrame); //false
this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId); //proxy.id

//该方法是panel drag事件的真正起始处理入口
9.Ext.dd.DragDrop.handleMouseDown
var me = this;
//可以通过指定primaryButtonOnly=true,来保证当鼠标点击icon button不会触发drag
if ((me.primaryButtonOnly && e.button != 0) || me.isLocked()) {
return;
}
//重置当前所有的dd对象的位置信息
me.DDMInstance.refreshCache(me.groups);
//me.DDMInstance.isOverTarget方法是判断当前鼠标的位置是否在 me 的之上(over)
if (me.hasOuterHandles || me.DDMInstance.isOverTarget(e.getPoint(), me)) {
if (me.clickValidator(e)) {
// set the initial element position
me.setStartPosition();
me.b4MouseDown(e);
me.onMouseDown(e);
//接下来就是将当前的me传给dragCurrent ,之后的mousemove事件就可以开始了
me.DDMInstance.handleMouseDown(e, me);
me.DDMInstance.stopEvent(e);
}
}

10.Ext.dd.DragDropManager.handleMouseDown
handleMouseDown: function(e, oDD) {
var me = this,
el;

if (Ext.quickTipsActive){
Ext.tip.QuickTipManager.ddDisable();
}
if (me.dragCurrent){
me.handleMouseUp(e);
}

me.currentTarget = e.getTarget();
me.dragCurrent = oDD;// 就是他,接下来了我们看看dragCurrent是怎么被使用的

el = oDD.getEl();
if (Ext.isIE9m && el.setCapture) {
el.setCapture();
}

// track start position
me.startX = e.getPageX();
me.startY = e.getPageY();

me.deltaX = me.startX - el.offsetLeft;
me.deltaY = me.startY - el.offsetTop;

me.dragThreshMet = false;

me.clickTimeout = setTimeout(
function() {
//clickTimeThresh(默认350毫秒之后,也就是说,当你按住鼠标350毫秒之后就会发生情况)毫秒之后会执行函数startDrag
//(见如下分析)
me.startDrag(me.startX, me.startY);
},
me.clickTimeThresh
);
}
//---------------------------------------------接下来的分析就是鼠标down的时候所发生的事情------------------------------------------

//在这个方法里current 就是我们上一个方法注入的dragCurrent
11.Ext.dd.DragDropManager.startDrag
startDrag: function(x, y) {
var me = this,
current = me.dragCurrent,
dragEl;

clearTimeout(me.clickTimeout);
if (current) {
//就是Ext.panel.DD中的b4StartDrag方法(见如下分析)
current.b4StartDrag(x, y);
//需要自己实现,默认情况下Ext.panel.DD为Ext.emptyFn
current.startDrag(x, y);
//见14分析。获取克隆panel的EL
dragEl = current.getDragEl();

// Add current drag class to dragged element
if (dragEl) {
//给克隆panel加上一些样式,该样式可以在Ext.panel.DD的属性中定义
Ext.fly(dragEl).addCls(me.dragCls);
}
}
me.dragThreshMet = true;
},

12.Ext.panel.DD.b4StartDrag
//还记的那个proxy吗?在Ext.panel.DD的构造函数里有这么一行代码:
//me.panelProxy = new Ext.panel.Proxy(panel, cfg)
b4StartDrag: function(x, y) {
//见如下代码分析
this.panelProxy.show();
}

13.Ext.panel.Proxy.show
show: function(){
var me = this,
panelSize;

if (!me.ghost) {
panelSize = me.panel.getSize();
me.panel.el.setVisibilityMode(Ext.Element.DISPLAY);
//显示一个克隆 panel
me.ghost = me.panel.ghost();
if (me.insertProxy) {
//在me.panel.dom的before位置插入一个DIV,(默认情况下是一个虚线框)
me.proxy = me.panel.el.insertSibling({cls: Ext.baseCSSPrefix + 'panel-dd-spacer'});
me.proxy.setSize(panelSize);
}
}
}

14. Ext.panel.DD.getDragEl
//具体克隆panel的处理在Ext.panel.Panel.ghost()
getDragEl : function(e){
var ghost = this.panelProxy.ghost;
if (ghost) {
return ghost.el.dom;
}
}

//---------------------------------------------接下来的分析就是鼠标move的时候所发生的事情------------------------------------------
//该方法是drag move事件的入口处理函数
15. Ext.dd.DragDropManager.handleMouseMove
handleMouseMove: function(e) {
var me = this,
current = me.dragCurrent,
diffX,
diffY;

if (!current) {
return true;
}
//dragThreshMet默认为false
if (!me.dragThreshMet) {
diffX = Math.abs(me.startX - e.getPageX());
diffY = Math.abs(me.startY - e.getPageY());
//下面if处理,也是我开始说过的,不能过于频繁执行move事件处理,例如不能拖动一个像素也出发move事件,
//所以clickPixelThresh,clickPixelThresh两个属性就是用来设置这种限制的
if (diffX > me.clickPixelThresh || diffY > me.clickPixelThresh) {
//详见11分析
me.startDrag(me.startX, me.startY);
}
}
//此时的dragThreshMet = true(看看11的最后一行代码)
if (me.dragThreshMet) {
current.b4Drag(e);//开始执行真正的移动**(见16)
current.onDrag(e);//开始执行真正的移动**
if (!current.moveOnly) {
me.fireEvents(e, false);
}
}

me.stopEvent(e);

return true;
}
16.Ext.dd.DD.b4Drag
b4Drag: function(e) {
//(见17分析)
this.setDragElPos(e.getPageX(), e.getPageY());
}

17.Ext.dd.DD.setDragElPos
setDragElPos: function(iPageX, iPageY) {
var el = this.getDragEl();
this.alignElWithMouse(el, iPageX, iPageY);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值