對開發者來說ExtJS的一個最大的交互性設計模式就是拖拽,使用拖拽的時候,無需有太多的思考,直接做好就行了。五步可以優雅的完成一個拖拽。
拖拽的定義
拖操作的意思就是鼠標在一個UI界面上按住鼠標左鍵不放,并且移動。拽操作是在拖操作中鼠標釋放的時候觸發的操作。抽象層次上,下圖就可以對拖拽進行一個總結。
為了加速開發者的開發,有一個Ext.dd類專門管理基礎決策,本指導中,覆蓋到的是拖拽操作的移除,非法拖拽修復和拖拽成功之后的處理。
組織拖拽類
第一眼看到Ext.dd類,你可能會被嚇到。但是當我們細看的時候就會發現他們都是從FragDrop類分支來的,并且大部分都可以歸為Drag和Drop組,繼續深挖,在拖拽交互中可以發現有single和multi組織區分。
我們先通過插入到DOM節點的方式學習single拖拽交互,我們會利用DD和DDTarget類,它實現了拖拽操作,我們需要先明確性我們的目標。
目標
需求就是說,有家租車公司,提供cars和trucks,有三種狀態,租賃,維修和正常。需要將不同狀態的車放在三個不同的容器中。
我們使用DD讓cars和trucks可以拖拽。我們需要創建三個容納拖拽目標容器(DDTarget),最后,使用不同的拖拽分組確保不同的車放在不同的分組中。
第一步:開始于drag
在實例化DD的時候,獲取循環div數字元素設置為可拖拽。
Ext.onReady(function() {
??????? // Create anobject that we'll use to implement and override drag behaviors a little later
??????? var overrides ={};
??????? // Configure thecars 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 theoverrides 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);
??????? });
??? });
這一步,定義了一個空的overrides,存放一系列的需要的action,通過DomQuery方法獲取子div元素,我們通過創建DD實例來讓車可拖拽,最后通過Ext.apply將事件都放置到dd里面。
窺視拖曳節點如何受到影響
當你拖拽汽車或卡車周圍的元素時,你會發現的第一件事就是它們會粘在任何被丟棄的地方。這是可以的,因為我們剛剛開始實施。重要的是要了解拖曳節點是如何受到影響的。這將有助于我們在返回到它們原來的位置時,將它們丟棄到任何不是有效的丟棄目標的對象上,這被稱為“無效滴”。下面的插圖使用Fixbug的HTML檢查面板,并突出顯示當拖動操作應用到CAMARO元素時所做的更改。
在拖動操作期間檢查拖拽元素時,我們可以看到一個樣式屬性添加到元素中,填充了三個CSS值:位置、頂部和左側。進一步的檢查表明,當節點被拖動時,位置屬性被設置為相對和左上和左屬性更新。在拖動手勢完成之后,樣式屬性與其中包含的樣式保持一致。這是我們必須清理的代碼,當我們修復一個無效的下降。在我們設置正確的丟棄目標之前,所有的丟棄操作都被認為是無效的。
第二步 修復無效拖拽
最不抵抗的路徑是通過復位拖動操作期間應用的樣式屬性來修復無效的刪除。這意味著拖曳元素會從鼠標下方消失,并重新出現在它起源的地方,將會非常乏味。為了使它更平滑,我們將使用Ext.FX動畫這個動作。請記住,拖放類被設計為具有重寫的方法。要實現修復,我們需要重寫B4StastRoad、OnValueDLoad和EnDRAW方法。讓我們將下面的方法添加到上面的覆蓋對象中,我們將討論它們是什么和做什么。
var overrides = {
??????? // Called theinstance the element is dragged.
??????? b4StartDrag :function() {
??????????? // Cache thedrag element
??????????? if (!this.el){
??????????????? this.el =Ext.get(this.getEl());
??????????? }
??????????? //Cache theoriginal XY Coordinates of the element, we'll use this later.
???????????this.originalXY = this.el.getXY();
??????? },
??????? // Called whenelement is dropped in a spot without a dropzone, or in a dropzone withoutmatching a ddgroup.
??????? onInvalidDrop :function() {
??????????? // Set a flagto invoke the animated repair
???????????this.invalidDrop = true;
??????? },
??????? // Called whenthe drag operation completes
??????? endDrag :function() {
???? ???????// Invoke the animation if theinvalidDrop flag is set to true
??????????? if(this.invalidDrop === true) {
??????????????? // Removethe drop invitation
???????????????this.el.removeCls('dropOK');
??????????????? // Createthe animation configuration object
??????????????? varanimCfgObj = {
???????????????????easing?? : 'elasticOut',
???????????????????duration : 1,
???????????????????scope??? : this,
???????????????????callback : function() {
???????????????????????// Remove the position attribute
???????????????????????this.el.dom.style.position = '';
??????????????????? }
??????????????? };
??????????????? // Applythe repair animation
???????this.el.setXY(this.originalXY, animCfgObj);
??????????????? delete this.invalidDrop;
??????????? }
??????? },
在上面的代碼中,我們首先重寫B4StastRoad方法,它被稱為拖拽元素在屏幕周圍被拖動的瞬間,并使它成為緩存拖拽元素和原始XY坐標的理想位置,稍后我們將在這個過程中使用它。接下來,我們重寫ValueDLoad,這是在拖動節點被丟棄到參與同一拖放組的丟棄目標之外的任何對象時調用的。此重寫只需將本地無效DROPULL屬性設置為true,將在下一個方法中使用。我們重寫的最后一個方法是EntDRAW,當拖動元素不再被拖動在屏幕周圍,拖拽元素不再被鼠標移動控制時被調用。此覆蓋將使用動畫將拖曳元素移回其原來的X和Y位置。我們在動畫結束時配置動畫,使用彈出式放松來提供一個涼爽有趣的彈跳效果。
第三步 配置拖拽目標
我們的要求規定,我們將允許汽車和卡車掉落在租用和修理集裝箱以及他們各自的原始集裝箱。要做到這一點,我們需要實例化DDATAL類的實例。這就是它的做法。
// Instantiate instances of Ext.dd.DDTarget for the cars andtrucks container
var carsDDTarget = Ext.create('Ext.dd.DDTarget','cars','carsDDGroup');
var trucksDDTarget = Ext.create('Ext.dd.DDTarget', 'trucks','trucksDDGroup');
// Instantiate instances of DDTarget for the rented and repairdrop 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 participatein the trucksDDGroup
rentedDDTarget.addToGroup('trucksDDGroup');
repairDDTarget.addToGroup('trucksDDGroup');
在上面的代碼片段中,我們已經為汽車、卡車、租賃和修復元素設置了下降目標。注意,CARS容器元素只參與“CARSDDGROUP”,卡車容器元素參與“TrutksDDGROUP”。這有助于強制要求汽車和卡車只能掉落在原容器中。接下來,我們實例化租用和修復元素的實例DDATAL。最初,它們被配置為只參與“CARSDDGROUP”。為了允許他們參與“TrutksDD群”,我們必須通過AdtoToGy添加它。好,現在我們已經配置了我們的下降目標。讓我們看看當我們把汽車或卡車停在一個有效的掉落元件上時會發生什么。
在執行跌落目標時,我們看到拖曳元素恰好保持其下降。也就是說,圖像可以落在落下目標上的任何地方,并停留在那里。這意味著我們的輟學實現還沒有完成。為了完成它,我們需要通過“另一個重寫”來實現“完全刪除”操作的代碼,這是我們之前創建的DD實例的另一個重寫。
第四步 完成拖拽
要完成該拖拽,我們需要使用DOM工具將元素從父元素實際拖動到DROP目標元素。這是通過重寫DD OnDracDRAP方法實現的。將下列方法添加到重寫對象中。
var overrides = {
??? ...
??? // Called uponsuccessful drop of an element on a DDTarget with the same
??? onDragDrop :function(evtObj, targetElId) {
??????? // Wrap the droptarget element with Ext.Element
??????? var dropEl =Ext.get(targetElId);
??????? // Perform thenode move only if the drag element's
??????? // parent is notthe same as the drop target
??????? if(this.el.dom.parentNode.id != targetElId) {
??????????? // Move theelement
???????????dropEl.appendChild(this.el);
??????????? // Remove thedrag invitation
???????????this.onDragOut(evtObj, targetElId);
??????????? // Clear thestyles
???????????this.el.dom.style.position ='';
???????????this.el.dom.style.top = '';
???????????this.el.dom.style.left = '';
??????? }
??????? else {
??????????? // This wasan invalid drop, initiate a repair
???????????this.onInvalidDrop();
??????? }
??? },
在上面的覆蓋中,拖動元素被移動到下拉目標元素,但僅當它與拖動元素的父節點不一樣時。在拖動元件移動之后,從其上清除樣式。如果DROP元素與拖拽元素的父元素相同,則通過調用SIT.OnSimultDROP確保修復操作發生。
一旦成功拖拽,拖拽元素現在將從它們的父元素移動到下拉目標。用戶如何知道它們是否懸停在有效的下降目標之上?我們將通過配置刪除邀請來給用戶一些視覺反饋。
第五步 拖放的校驗增加
為了使拖放更加有用,我們需要向用戶提供關于是否可以成功地進行跌落操作的反饋。這意味著我們必須重寫OnDrand和OnDRAWOUT方法,將這些最后兩種方法添加到重寫對象中。
var overrides = {
??????? ...
??????? // Only calledwhen the drag element is dragged over the a drop target with the
same ddgroup
??????? onDragEnter :function(evtObj, targetElId) {
??????????? // Colorizethe 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 {
??????????????? // Removethe invitation
???????????????this.onDragOut();
??????????? }
??????? },
??????? // Only calledwhen element is dragged out of a dropzone with the same ddgroup
??????? onDragOut :function(evtObj, targetElId) {
???????????this.el.removeCls('dropOK');
??????? }
};
在上面的代碼中,我們重寫了OnDrand和OnDRAWOUT方法,這兩種方法只在拖拽元素與參與同一拖拽組的下拉目標交互時才使用。只有當鼠標指針與拖動目標的邊界相交,而拖動項處于拖動模式時,才調用OnDRAGTENT方法。同樣,當拖動模式下鼠標光標首先被拖動到下拉目標的邊界之外時,調用OnDRAGOUT。
通過向OnDrand和OnDRAGOUT方法添加重寫,我們可以看到當鼠標光標首先相交一個有效的下拉目標時,拖動元素的背景會變綠,并且當它離開下拉目標或被丟棄時將失去其綠色背景。這就完成了DOM元素拖放的實現。