ExtJS 4 拖放[drag and drop]

定义拖放


一个拖动操作,就是在某个页面元素上按下鼠标并移动。一个放下操作,就是在拖动动作之后放开鼠标。可以从下图来看:

Ext JS 的Ext.dd 类中定义了基本的拖放操作。

拖放类的组织


所有的拖放类基本上都归类到Drag or Drop 组中

手头的任务


这里的例子是一个租车公司,把轿车和卡车有三种状态: 闲置,租用和修理。该应用就是可以在这三个状态

使用DD,让可选轿车和卡车可以拖动; 而后,使用 DDTarget,让租车和修理的容器可以接受拖放;最后使用不同的拖放组让轿车和卡车只能拖到指定的区块。

Step 1: 开始拖动
让以上可选的轿车和卡车可以拖动:

Ext.onReady(function() {  
    // Create an object that we'll use to implement and override drag behaviors a little later  
    var overrides = {};  
  
    // Configure the cars to be draggable  
    var carElements = Ext.get('cars').select('div');  
    Ext.each(carElements.elements, function(el) {  
        var dd = Ext.create('Ext.dd.DD', el, 'carsDDGroup', {  
            isTarget  : false  
        });  
        //Apply the overrides object to the newly created instance of DD  
        Ext.apply(dd, overrides);  
    });  
  
    var truckElements = Ext.get('trucks').select('div');  
    Ext.each(truckElements.elements, function(el) {  
        var dd = Ext.create('Ext.dd.DD', el, 'trucksDDGroup', {  
            isTarget  : false  
        });  
        Ext.apply(dd, overrides);  
    });  
});   

以上使用DomQuery的方式找到需要拖动的区块,并针对里面的子元素创建了DD的实例。
对于轿车和卡车用了不同的分组。(可以拖放的位置不同)
这里定义了一个空的对象 overrides, 并且把这个对象通过Ext.apply应用到创建的 DD 的对象上。

看一下拖动效果的实质

从以上可以看出, 一个元素有三个CSS的属性; position, top 和 left;

Step 2: 修复无效的拖放
最简单的修复方式就是,拖动失败之后让样式恢复到拖动前的状况,这样的处理看起来有点枯燥,所以,可以用Ext.Fx 添加动画的效果。还记得拖放类的设计有overridden 的方法,要实现这个效果,需要覆写b4StartDrag, onInvalidDrop 和endDrag这些方法。看下例:

var overrides = {  
    // Called the instance the element is dragged.  
    b4StartDrag : function() {  
        // Cache the drag element  
        if (!this.el) {  
            this.el = Ext.get(this.getEl());  
        }  
  
        //Cache the original XY Coordinates of the element, we'll use this later.  
        this.originalXY = this.el.getXY();  
    },  
    // Called when element is dropped not anything other than a dropzone with the same ddgroup  
    onInvalidDrop : function() {  
        // Set a flag to invoke the animated repair  
        this.invalidDrop = true;  
    },  
    // Called when the drag operation completes  
    endDrag : function() {  
        // Invoke the animation if the invalidDrop flag is set to true  
        if (this.invalidDrop === true) {  
            // Remove the drop invitation  
            this.el.removeCls('dropOK');  
  
            // Create the animation configuration object  
            var animCfgObj = {  
                easing   : 'elasticOut',  
                duration : 1,  
                scope    : this,  
                callback : function() {  
                    // Remove the position attribute  
                    this.el.dom.style.position = '';  
                }  
            };  
  
            // Apply the repair animation  
            this.el.moveTo(this.originalXY[0], this.originalXY[1], animCfgObj);  
            delete this.invalidDrop;  
        }  
    },  

效果:

Step 3: 配置放下的目标(drop targets)

// Instantiate instances of Ext.dd.DDTarget for the cars and trucks container  
var carsDDTarget = Ext.create('Ext.dd.DDTarget', 'cars','carsDDGroup');  
var trucksDDTarget = Ext.create('Ext.dd.DDTarget', 'trucks', 'trucksDDGroup');  
  
// Instantiate instnaces of DDTarget for the rented and repair drop target elements  
var rentedDDTarget = Ext.create('Ext.dd.DDTarget', 'rented', 'carsDDGroup');  
var repairDDTarget = Ext.create('Ext.dd.DDTarget', 'repair', 'carsDDGroup');  
  
// Ensure that the rented and repair DDTargets will participate in the trucksDDGroup  
rentedDDTarget.addToGroup('trucksDDGroup');  
repairDDTarget.addToGroup('trucksDDGroup');  

这里代码为 轿车,卡车,租赁和修理的区块添加了drop targets. 轿车容器仅接收"carsDDGroup" 的drag, 卡车容器仅接收“trucksDDGroup”;
接着定义了rentedDDTarget 和repairDDTarget并且只接收 “carsDDGroup”, 为了让“trucksDDGroup”也可以,通过 addToGroup 方式进行添加。
效果:

以上的拖放可以拖放到指定位置,可能出现重叠等问题。

Step 4: 继续 完成Drop

var overrides = {  
    ...  
    // Called upon successful drop of an element on a DDTarget with the same  
    onDragDrop : function(evtObj, targetElId) {  
        // Wrap the drop target element with Ext.Element  
        var dropEl = Ext.get(targetElId);  
  
        // Perform the node move only if the drag element's  
        // parent is not the same as the drop target  
        if (this.el.dom.parentNode.id != targetElId) {  
  
            // Move the element  
            dropEl.appendChild(this.el);  
  
            // Remove the drag invitation  
            this.onDragOut(evtObj, targetElId);  
  
            // Clear the styles  
            this.el.dom.style.position ='';  
            this.el.dom.style.top = '';  
            this.el.dom.style.left = '';  
        }  
        else {  
            // This was an invalid drop, initiate a repair  
            this.onInvalidDrop();  
        }  
    },  

效果:

Step 5 : 添加放下邀请(Drop Invitation)
当拖放操作完成时,添加一些反馈信息给用户。
重写onDragEnter 和onDragOut 方法。

var overrides = {  
    ...  
    // Only called when the drag element is dragged over the a drop target with the same ddgroup  
    onDragEnter : function(evtObj, targetElId) {  
        // Colorize the drag target if the drag node's parent is not the same as the drop target  
        if (targetElId != this.el.dom.parentNode.id) {  
            this.el.addCls('dropOK');  
        }  
        else {  
            // Remove the invitation  
            this.onDragOut();  
        }  
    },  
    // Only called when element is dragged out of a dropzone with the same ddgroup  
    onDragOut : function(evtObj, targetElId) {  
        this.el.removeCls('dropOK');  
    }  
};  

作者: oscar999
原文: http://blog.csdn.net/oscar999/article/details/9561883