5 шагов к пониманию Drag and Drop в Ext JS

Метки: extjs, js, drag and drop

5 Steps to Understanding Drag and Drop with Ext JS 5 Steps to Understanding Drag and Drop with Ext JS

Один из наиболее мощных визуальных паттернов, доступных разработчикам, является “Drag and Drop”. Мы постарались представить простой пример использования функции перетаскивания. Здесь представлены 5 шагов, которые это красиво реализуют.

Определение drag and drop
Действие перетаскивания (drag), обычно является нажатием с удержанием кнопки мыши на какой-либо графический элемент с его последующим перемещением. Действие бросания (drop) происходит когда вы отпускаете кнопку мыши.
На высоком уровне, решения операции drag and drop может быть представлено следующим образом:
5 шагов к пониманию Drag and Drop в Ext JS
Чтобы ускорить процесс разработки, Ext JS представляет классы Ext.dd для управления простыми решениями. В этой статье мы разберемся с кодом для обработки перетаскивания, некорректного бросания объекта и что происходит при успешном перетаскивании.

Организация классов drag and drop
На первый взгляд, классы в документации Ext.dd могут казаться отпугивающими. Но, если мы посмотрим на эти классы, мы увидим, что они все походят от DragDrop и их можно разбить на категории в группы Drag или Drop. Копнув еще чуть глубже, становится ясно, что классы могут относится к одной ноде либо к множественным взаимодействиям drag или drop.
5 шагов к пониманию Drag and Drop в Ext JS
Для того чтобы изучить основы перетаскивания мы сосредоточимся на принятии единственного взаимодействия drag and drop к нодам DOM. Для этого мы будем использовать классы DD и DDTarget, которые дают базовую реализацию поведения перетаскивания.
В любом случае, перед тем как что-либо реализовывать нужно определится с целями.

Постановка задачи
Давайте рассмотрим задачу, когда требуется разработать приложение для автомобильной компании, сделать возможность размещать машины, которые могут принимать одно из трех состояний: available (доступна), rented (занята) или repair (на ремонте). Машины и грузовики могут быть помещены только в свой соответствующий контейнер.
5 шагов к пониманию Drag and Drop в Ext JS

Для начала, мы должны сделать наши машины и грузовики «перетаскиваемыми». Для этого мы воспользуемся DD. Нам нужно сделать контейнеры для последующего заполнения: rented, repair и vehicle.
Пример HTML и CSS для этого примера уже построен, вы можете посмотреть его здесь. Используя данную страницу мы сможем добавлять код для операций перетаскивания.

Шаг 1: Начнем с перетаскивания
Для того чтобы сделать элементы дивов «перетаскиваемыми», нам нужно получить список и обойти его, инициализируя новые сущности DD. Вот как мы это делаем:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Создаем объект, который мы будем использовать чуть позже
// для реализации поведения перетаскивания 
var overrides = {};
// Делаем машины перетаскиваемыми
var carElements = Ext.get('cars').select('div');
Ext.each(carElements.elements, function(el) {
    var dd = new Ext.dd.DD(el, 'carsDDGroup', {
        isTarget  : false
    });
    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);
});

Все классы drag and drop были сделаны с учетом переопределения их методов. По этой причине мы создавали пустой объект overrides, который мы будем заполнять в дальнейшем для переопределения нужных нам действий.
Мы получили список элементов машин и грузовиков с помощью DomQuery, для всех дочерних Div-элементов.
Для того чтобы сделать элементы машин и грузовиков перетаскиваемыми, мы создаем новую сущность DD, указывая элементу, что он может перетаскиваться, а также сообщаем группе, что элемент стал ее частью. Это пригодится нам позже, когда мы установим два контейнера для перетаскивания - rented и repair.
Перед тем как продолжить реализацию, давайте проанализируем, что сейчас происходит при перетаскивании элемента по экрану. С пониманием этого, остальная реализации будет проще.

Захват и перемещение нод
Первое, на что вы обратите внимание, это что перемещаемые объекты остаются там, где вы их бросаете. Пока об этом не стоит беспокоится, так как мы только начали реализовывать скрипт. Важно понять каким образом перемещаемые ноды захватываются.
Это поможет нам при написании возврата к начальной точке, в случае, если объект был брошен на некорректную область.
5 шагов к пониманию Drag and Drop в Ext JS
Демо

Наблюдая за перетаскиваемым элементом во время операции перемещения, вы можете видеть как меняются атрибуты стиля данного элемента, а именно: position, top и left. Установлено относительное позиционирование, то есть position — relative, а атрибуты left и top обновляются во время перемещения. После того как перемещение завершилось, атрибуты принимают данные своей текущей позиции. Это то, что нам нужно исправить, при перетаскивании в неправильную область. До тех пор пока мы это не сделаем, все действия по бросанию объекта будут некорректны.

Шаг 2: Исправляем некорректное бросание
При бросании в некорректной области объект должен исчезать из того места где его бросили и появляться там откуда его начали тащить. Выглядеть это будет довольно уныло, так что давайте лучше сделаем возврат объекта с помощью красивой анимации. Для анимации действия будем использовать Ext.Fx. Помните, что классы drag and drop создавались с учетом переопределения методов. Для реализации нам нужно переопределить методы b4StartDrag, onInvalidDrop и endDrag. Давайте добавим следующие методы к нашему объекту overrides и обсудим, что они делают:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Вызываем сущность перетаскиваемого элемента
b4StartDrag : function() {
    // запоминаем перетаскиваемый элемент
    if (!this.el) {
        this.el = Ext.get(this.getEl());
    }
 
    // Запоминаем изначальные координаты XY, мы будем использовать это позже
    this.originalXY = this.el.getXY();
},
// Вызываем когда элемент был брошен некорректно
onInvalidDrop : function() {
    // Устанавливаем флаг для анимации
    this.invalidDrop = true;
},
// Вызываем при завершении операции перетаскивания
endDrag : function() {
    // Делаем анимацию в случае установленного флага invalidDrop как true
    if (this.invalidDrop === true) {
        // Убираем drop invitation
        this.el.removeClass('dropOK');
 
        // Создаем объект конфигурации анимации
        var animCfgObj = {
            easing   : 'elasticOut',
            duration : 1,
            scope    : this,
            callback : function() {
                // Убираем атрибут position
                this.el.dom.style.position = '';
            }
        };
 
        // Применяем анимацию
        this.el.moveTo(this.originalXY[0], this.originalXY[1], animCfgObj);
        delete this.invalidDrop;
    }
 
},

В вышеприведенном коде мы начали с переопределения метода b4StartDrag, который вызывается сразу после начала перетаскивания элемента, это идеальное место для хранения изначальных координат XY, которые мы будем использовать позже.
Далее мы переопределяем метод onInvalidDrop, который вызывается когда элемент был брошен на область не предназначенную для этого, либо которая относится к другой группе. По сути, в этом методе мы просто устанавливаем свойство invalidDrop как true, это будет использоваться в следующем методе.
Последний метод, который мы переопределили это endDrag, который вызывается когда перетаскиваемый элемент уже не перетаскивается по экрану и уже не контролируется движениями мыши. Метод перемещает элемент на его начальную позицию, используя анимацию. Мы настроили анимацию для использования elasticOut чтобы сделать крутую фишку с эффектом в конце анимации.
5 шагов к пониманию Drag and Drop в Ext JS
Демо

Хорошо, теперь все фиксы сделаны и нам нужны области для бросания элементов, чем мы сейчас и займемся.

Шаг 3: Настройка областей бросания
Поставленная задача требует чтобы мы позволяли машинам и грузовикам быть помещенными в контейнеры rented и repair. Для этого нам понадобится создать сущность класса DDTarget.
Вот как это делается:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
//Создаем сущности Ext.dd.DDTarget для контейнера машин и грузовиков
var carsDDTarget    = new Ext.dd.DDTarget('cars','carsDDGroup');
var trucksDDTarget = new Ext.dd.DDTarget('trucks', 'trucksDDGroup');
 
//Создаем сущности DDTarget для бросания элементов в rented и repair
var rentedDDTarget = new Ext.dd.DDTarget('rented', 'carsDDGroup');
var repairDDTarget = new Ext.dd.DDTarget('repair', 'carsDDGroup');
 
//rented и repair DDTargets будут относится к группе trucksDDGroup 
rentedDDTarget.addToGroup('trucksDDGroup');
repairDDTarget.addToGroup('trucksDDGroup');

В вышеприведенном коде мы сделали область для бросания машин, грузовиков и элементов rented и repair. Обратите внимание, что контейнер для машин относится только к “carsDDGroup”, а грузовики только к “trucksDDGroup”. Это позволит достигнуть того, что машины и грузовики смогут быть брошены только в предназначенные для этого контейнеры.
Далее мы создаем сущности DDTarget для элементов rented и repair. Изначально они сконфигурированы чтобы относиться к группе “carsDDGroup”. Если мы хотим позволить относиться к группе грузовиком, их нужно добавить в группу “trucksDDGroup” с помощью addToGroup.
Хорошо, теперь мы настроили места для бросания и можем посмотреть, что будет происходить при бросании элементов на валидную зону.
5 шагов к пониманию Drag and Drop в Ext JS
Демо

При бросании элементов мы видим, что перетаскиваемый элемент остается в точности на том же месте, где мы его бросили. Рисунки могут быть брошены где угодно и они там остаются. Это означает, что функция бросания еще не завершена.
Чтобы закончить ее нам нужно реализовать код действия «завершения бросания», что означает еще одно переопределение сущностей DD, которые мы создавали.

Шаг 4: Завершение бросания
Для завершения бросания нам потребуется использовать инструменты DOM для перемещения элементов. Это подразумевает переопределение DD метода onDragDrop. Добавьте следующие методы к объекту:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Вызывается при успешном бросании на DDTarget
onDragDrop : function(evtObj, targetElId) {
 
    var dropEl = Ext.get(targetElId);
 
    // Представляет движении только в случае,
// если родитель перемещаемого элемента
// не является той же нодой
    if (this.el.dom.parentNode.id != targetElId) {
 
        dropEl.appendChild(this.el);
 
        this.onDragOut(evtObj, targetElId);
 
        // Очищаем стили
        this.el.dom.style.position ='';
        this.el.dom.style.top = '';
        this.el.dom.style.left = '';
    }
    else {
        // Некорректное бросания, инициализация возврата
        this.onInvalidDrop();
    }

В вышеприведенном коде, перемещаемый элемент двигается к зоне бросания, но только в том случае если элемент не является родительским узлом. После перемещения элемента, его стили очищаются.
Если перемещаемый элемент тот же, что и его родительский узел, мы инициализируем возврат, вызовом this.onInvalidDrop.
5 шагов к пониманию Drag and Drop в Ext JS
Демо

После успешного броска, перемещаемые элементы теперь будут перемещены от их родительских элементов к зоне бросания.
Как пользователь будет знать где можно бросать элемент, а где нельзя? Мы дадим ему визуальную подсказку, настроив приглашение к бросанию.

Шаг 5: Добавляем приглашение к бросанию
Чтобы сделать drag and drop более удобным, нам необходимо давать знак пользователю, можно ли бросить на этой области элемент или нет. Это означает, что нам нужно переопределить методы onDragEnter и onDragOut.
Добавьте эти два метода таким образом:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Вызывается только в случае,
// если перетаскиваемый элемент находится над зоной из той же ddgroup
onDragEnter : function(evtObj, targetElId) {
    // Подсвечиваем перетаскиваемый элемент,
// если его родитель не является областью бросания
    if (targetElId != this.el.dom.parentNode.id) {
        this.el.addClass('dropOK');
    }
    else {
        // Убираем приглашение
        this.onDragOut();
    }
},
// Выполняется только в случае, 
//когда перетаскиваемый элемент выходит за пределы  области бросания из той же ddgroup
onDragOut : function(evtObj, targetElId) {
    this.el.removeClass('dropOK');
}

В данном коде мы описали методы onDragEnter и onDragOut, которые используются в случаях когда перетаскиваемый элемент взаимодействует с областью бросания, которая относится к той же группе.
Метод onDragEnter вызывается при первом пересечении границ области бросания. Соответственно, метод onDragOut вызывается когда курсор первый раз уходит за границы области бросания.
5 шагов к пониманию Drag and Drop в Ext JS
Демо

Как вы видите, после добавление методов onDragEnter и onDragOut, цвет фона перетаскиваемого элемента меняется на зеленый, как только пересекаются границы области бросания.
На этом наша реализация drag and drop с элементами DOM подошла к концу.

На этом все не заканчивается
Эффект drag and drop может быть применен почти ко всему в Ext JS. Вот пара примеров как можно применять данный эффект в некоторых популярных виджетах:
Пример Drag and Drop
Второй пример Drag and Drop

Оригинал: 5 Steps to Understanding Drag and Drop with Ext JS

Рейтинг: 12345   << Вы можете поставить оценку этой статье

Обсуждение статьи:

 
Gonzih [2009-12-22]
Уважемый, огромное спасибо за статью, давно искал нечто столь вразумительное


ВААВААН [2010-02-24]
Ничё не понял, но обещаю исправиться)


AntonStrax [2010-04-22]
Как сделать чтобы елементы менять местами


Ankhbayar.G [2010-08-10]
Very good sample. Spasibo!


мир [2010-12-14]
а как сделать что б вставлять контейнер с картинками один в другой?


Алексей [2011-01-21]
Спасибо. Очень хороший туториал - грамотно и подробно описаны главные действия.


Одиночка Айс [2011-03-05]
Круто, конечно, спасибо за статью. Но вот как реализовать туже самую возможность, но выделив весь js-код в отдельный файл? Пробовал, не получается. Буду рад подсказкам.


Одиночка Айс [2011-03-05]
Сорри, разобрался. Еще раз большое спасибо за статью


Иван [2011-04-22]
Спасибо за диаграмму классов, навел порядок


 

Оставить комментарий:

Имя


E-mail


Сообщение