五步掌握Ext的拖放(上)


五步掌握Ext的拖放
5 Steps to Understanding Drag and Drop with Ext JS

September 13, 2009 by Jay Garcia 翻译:frank
那么多的交互设计模式中,“拖放(Drag and Drop)”模式是开发者感觉比较不错的。用户日常在进行拖放操作的时候,真的是想都不用想地就可以轻松搞掂了,易学易用,非常直观。下文中,不是我们断言,只要将下面五个步骤的要义领会在心,拖放不再是一间难事。
定义拖放拖动(drag)的动作,就是鼠标的点击动作发生了,点击在某些UI元素身上,就可以按着不放,同时也可以移动着鼠标;放下(drop)的动作,就是在拖动动作开始后,但鼠标按钮松开了——就是放下的动作。
宏观而言,拖放的操作可用下面的流程图来描述。
605,421
为加速开发,Ext JS本来就提供了 Ext.dd以实现拖放的支持。在本博文中,我们将围绕拖放行动的“触发、无效置下和成功置下”的这几个方面问题,以及其中事件消息编程,还有修改外观的问题进行讨论。
拖放类的组织构成初涉Ext.dd的文档部分大家觉得会有些怕怕的,但是,如果我们稍加一点耐心去观察其中的源码,那么我们会发现许多类都继承于DragDrop类,并由此分为“Drag拖动的”和“Drop置下的”这两大类别。分类所针对的对象就是单个或多个节点其拖放的行为模式,——从类的组织来看可以看到这样的结果。
549,379
我们打算从最简单的DOM节点单个拖放的知识点来展开讨论,为的是更好地降低的学习曲线,适合初学者。切入正题,那就是分别利用DDDDTarget类扩展出基本的自定义拖放行为。
然而,我们必须先介绍一下例程以及例程的目标是如何。
话说这个例子……假设有这样的一个需求,客户是一个间汽车出租公司,出租cars小汽车和trucks卡车。他希望这些汽车有三种状态可选择:available可用的、rented已出租的、in repaire处于维修中的。作为类别划分,cars和trucks分别只能在其所属的“available”容器中。
703,334
一开始,第一目标是使得cars和trucks“可拖动”的状态,对此我们将会使用DD;第二,我们要让rentedrepair vehicle作为“可投放(可置下)”的目标地方,对此我们将会使用DDTarget。最后,我们将安排两个拖放组(drag drop groups)把小车和卡车区分开来,各自属于不同的“available”投放容器就不会相互搞错。
这个例程的HTML和CSS已经做好,可以在这儿下载。下载后,就可以开始拖动这些小车和卡车的操作了。
第一步:开始拖动要使得汽车所在的DIV可拖动,得先要获取这些DIV集合。获取后,集合是一个数组,我们遍历它以此建立DD实例。下面源码是该步骤的过程。

// 创建一个重写拖动行为的对象,稍后再试。
var overrides = {};
// 配置cars可拖动的
var carElements = Ext.get('cars').select('div');
Ext.each(carElements.elements, function(el) {
    var dd = new Ext.dd.DD(el, 'carsDDGroup', {
        isTarget  : false
    });
    // 复制overrides对象到刚刚创建的DD实例
    Ext.apply(dd, overrides);
});
 
var truckElements = Ext.get('trucks').select('div');
Ext.each(truckElements.elements, function(el) {
    var dd = new Ext.dd.DD(el, 'trucksDDGroup', {
        isTarget  : false
    });
    Ext.apply(dd, overrides);
});

所有的拖放类其参与的模式皆是“方法重写(methods overridings)”。上述声明了一个空的overrides的对象,以待后面安排的动作方法,就覆盖到这个对象身上。这就是强调代码签名(code segment)的原因。
欲查询cars容器中的全体子div元素,我们可使用DomQuery选取元素的函数。
要让cars和trucks元素变为可拖动的(dragable),我们创建了新的DD实例,所传入的参数就是cars和trucks的DOM元素,还有说明其所隶属于的拖放组,也就是说小汽车应对应着小汽车拖放组、卡车应对应着卡车拖放组。拖放组(group)是什么呢?先买个关子,——等下我们便知道拖放组怎么设置有多重要了,特别在定义rentedrepair置下目标的时候。
注意我们重写DD对象的时候使用了Ext.apply,这是一个增加对象的属性或方法的函数,十分方便。
在继续下一步之前,我们得花点时间分析一下拖动元素时屏幕将会发生些什么,进而明白所以然之后才会对接下来的代码在理解上,游刃有余。
说说拖动的元素怎么个拖动法先要说明的是,当移动car或truck的时候,移到哪就摆放在哪,因为我们例子是一步一步来,从简单到复杂,所以这时候的例子正是纯粹的“拖动(drag)”,也就是说我们把拖放的过程从中拆分了。那么移动或拖动到某个目的地是不是就是一个合法之目的地,符不符合我们程序限定之要求呢?这就是有效置下和无效置下的区别了。相对地,我们仅此理解了“无效置下(invalid drop)”即可清楚有效置下的逻辑。
下面插图来自FireBug的HTML检测面板,通过高亮的效果告知正在拖动的div元素,即Camaro元素(估计某个汽车品牌)。
575,331
点击上面的图片测试拖动操作。.
注意观察拖动的过程中,我们看到元素底下的style样式属性有了三个新的CSS值:position、top和left,其中定义position为相对位置relative便可以将元素在页面上移来移去。移到的同时,top、left的数值也在变化,不断更新(此二值为元素的参照坐标)。由此可见,拖动的原理就在于定义这三项值的问题上。
拖动手势完成之后,style属性仍保持不变,当然再拖动的话,style又会新变化,但按道理来说,我们应该不是这样,而是在无效drop的状态下令元素回到原来的位置。在设置好合法置下目标之前,任何置下动作都可被认为是无效的drop。
第二步:修复无效置下修复无效的drop,其实就是让拖动了的元素“归位”。怎么个“归位”法?既然前面修改了元素的style样式,那么“归位”就是恢复style为拖动之前的数值。最简单的制作方法,便是在鼠标按钮松开的事件触发后,拖动着的那个元素马上消失,然后在原来的位置重新出现。照这样简单的做法不是不行,就是显得比较生硬,最好还是用Ext.Fx 产生出渐变的过程,为求整个过程显得平滑一些。
提一提哪些方法我们要重写的repair方法:b4StartDrag, onInvalidDrop endDrag。
对overrides对象加入下列的方法。

// 获取在拖动的那个元素
b4StartDrag : function() {
    // Cache the drag element
    if (!this.el) {
        this.el = Ext.get(this.getEl());
    }
 
    // 先记下原始坐标,后面有用
    this.originalXY = this.el.getXY();
},
// 不在一个拖放组里面的就是无效drop
onInvalidDrop : function() {
    // Set a flag to invoke the animated
        repair
    this.invalidDrop = true;
},
// 拖动操作结束后执行
endDrag : function() {
    // 无效置下真正的逻辑
    if (this.invalidDrop === true) {
        // Remove the drop invitation
        this.el.removeClass('dropOK');
 
        // 定义动画的配置项对象
        var animCfgObj = {
            easing   : 'elasticOut',
            duration : 1,
            scope    : this,
            callback : function() {
                // Remove the position attribute
                this.el.dom.style.position = '';
            }
        };
 
        // 应用动画
        this.el.moveTo(this.originalXY[0], this.originalXY[1], animCfgObj);
        delete this.invalidDrop;
    }
 
},

首先是b4StartDrag方法。顾名思义,它开始拖放前被调用。某种程度上也是一个事件。它的作用是记下元素原来的XY坐标,也就是被拖动前的位置。归位的时候就凭着这个坐标;其次endDrag则是一个无效置下invalidDrop的通知方法。无效drop前面已经说过了,表示在不同一个DD组的区域投放,通通视作无效的投放。这里只是设置实例属性invalidDrop为true,简单地标识一下;最后一个重写的方法endDrag,每一次结束拖动的时候都会调用,此时元素不能通过鼠标的控制进行拖动了。如果不是成功地到达目的地,应该根据XY的坐标回到原来的位置。该例子中我们采用了elasticOut的动画美化一下效果。705,335
点击上面的图片就可以演示动画修饰过的修改过程。
OK,修复drop的过程就到此完成了。接下来的步骤是 创建置下对象,相当于“目的地、目标(drop target)”,形成置下的接纳点,并定义有效地置下过程。

转自:
http://blog.csdn.net/zhangxin09/archive/2010/05/03/5551665.aspx