一、定义拖放
从本质上讲,一个拖放操作是一个当鼠标按钮被压低并移动时对一些UI元素的点击姿态。拖动操作之后松开鼠标按钮时,放操作发生。从一个较高的层次上,拖拽可以总结为以下流程图。

二、组织拖放类

三.例子
为出租汽车公司开发一个应用程序,使他们能够把他们的汽车和卡车在可用、租借或修理三种状态之一。汽车和卡车只能被放置在他们各自的“可用”的容器中。

首先,我们必须使汽车和卡车可拖动。为此将使用DD。我们需要将租赁、维修和车辆容器作为放的目标。为此我们将使用DDTarget。最后,我们将使用不同拖放组来达到汽车和卡车只能扔进他们各自的“可用”容器的要求。
1.第一步:拖动
为了配置车辆的div元素使之可拖动,我们将需要获取一个列表,并遍历它实例化新的实例DD。
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);
});
}); 所有的拖放类都可以通过重写它的方法实现。所以在上面的代码段,我们创建一个空的被称为overides的对象,之后我们将用特定的动作来填补它。我们利用DomQuery选择方法得到汽车和卡车元素的列表来查询div元素的所有元素的汽车容器。为了使汽车和卡车元素可拖动,我们创建一个DD的实。车辆类型有其各自的拖放组。我们使用Ext.apply DD将overrides对象应用到新创建的实例DD。这是一种方便的方法来向一个已存在的对象添加属性和方法。
如何影响拖节点

检查在拖动操作的中拖动元素时,我们可以看到一个包括position,top and left的样式属性被添加到元素中。进一步检验表明,位置属性是相对的,当节点在附近拖动时,top 和 left属性改变。拖动完成后,样式属性仍然存在。这是当我们为无效的放置编码时必须清理的。除非我们设置适当的放置目标,所有drop操作被视为无效。
2.第二步:修复一个无效的放置
修复一个无效的放置时,最简单的方法是重置样式属性。这意味着拖动元素有可能会从鼠标下消失,再在起始位置重现,这将会相当无聊。使它更加流畅,我们将使用Ext.Fx这个行动有生气。为了实现修复,我们需要覆盖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;
}
}, 在上面的代码中,我们重写了b4StartDrag方法,当即时的拖动元素开始被拖在屏幕上时被调用,使其成为一个缓存的拖动元素和原始XY坐标系的理想地方接下来,我们重写了onInvalidDrop方法,这是被调用当拖动节点是放在了除了拖放目标之外的其他地方。这个覆盖简单地设置invalidDrop属性为真,它将用于下一个方法。我们覆盖的最后一个方法是endDrag,当拖动元素不再在屏幕上被拖且拖动元素不再被鼠标移动控制时,它被调用。这个覆盖将使用动画使拖动元素回到原来的X和Y位置。我们将配置动画以使用elasticOut为最后的动画提供一个很酷的和有趣的弹性效应。

3.第三步:配置drop targets
我们的需求表明我们将让汽车和卡车放在租来和修复的容器,以及各自的原来的容器。要做到这一点,我们需要实例化DDTarget类的实例。
// 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'); 在上面的代码片段中,我们为汽车、卡车、租赁和修复的元素设置放置目标。汽车容器元素只参与“carsDDGroup”,卡车容器元素参与“trucksDDGroup”。这有助于执行汽车和卡车只能被投到他们原始容器的要求。接下来,我们实例化租赁和修复元素的DDTarget实例。最初,它们配置为只参与“carsDDGroup”。为了让它们参与“trucksDDGroup”,我们必须将它添加addToGroup的方法。现在我们已经配置好放置目标。

拖动元素确保它确切的放置。这是,图片可以放置在放置目标的任何地方并呆在那。这意味着我们的drop实现并不完整。为了完成它,我们需要实际的“complete drop“操作的代码。
4.完成放置
为了完成放置,我们将需要使用DOM的工具实际地将元素从它的父元素拖动到放置目标元素。这可以通过覆盖DD onDragDrop方法来完成。
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();
}
}, 只有当放置目标元素与拖动元素的父节点不一样时,拖动元素被移动到放置目标元素。拖动元素被转移后,它的样式便被清除。如果放置目标元素与拖动元素的父节点一样时,我们通过调用this.onInvalidDrop来保证一个修复操作发生。
this.onInvalidDrop.

5.添加放置配置
我们需要覆盖onDragEnter和onDragOut方法,将这两个方法添加到overrides对象中。
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');
}
}; onDragEnter的方法在当拖动物品是在拖动模式且鼠标第一次与拖放目标的边界相交时调用。同样,onDragOut是在拖动模式且当鼠标是第一次被拖出拖放目标边界外时调用。
while in drag mode.

239

被折叠的 条评论
为什么被折叠?



