HTML5 и drag & drop нескольких объектов

от автора

Перетаскиванием объектов на HTML5 никого уже не удивишь, но все же попробую рассказать кое о чем интересном, а именно, как сделать красивое перетаскивание нескольких объектов, используя только HTML5.

На странице есть набор элементов типа А, которые можно по одному или группой перетащить в элемент типа Б. Пользователь должен видеть, перетаскивает он один элемент или несколько. Перетаскивать один элемент просто, достаточно присвоить свойству draggable значение true, наверняка, все это делали.

У меня уже готова страница с перетаскиванием «по-одному». Элементы с классом item можно выделить и перенести в элемент с классом dropzone (выделяем, кликнув по квадратику).

Демо некрасивого перетаскивания

Для выделения нужно кликнуть по элементу, перетаскивать можно только выделенные элементы, можно перетаскивать несолько элементов за один раз.

Структура страницы:

<div class="items-container">     <div class="items">         <div class="item"><span>a</span></div>         <div class="item"><span>b</span></div>         <div class="item"><span>c</span></div>         <div class="item"><span>d</span></div>         <div class="item"><span>e</span></div>         <div class="item"><span>f</span></div>         <div class="item"><span>g</span></div>     </div> </div>  <div class="dropzone-container">     <div class="dropzone"></div> </div> 

Код:

// jQuery убирает у объектов событий "лишние" свойства, поэтому, если мы хотим использовать HTML5 // примочки вместе с jQuery, нужно включить для событий свойство dataTransfer. jQuery.event.props.push('dataTransfer');  // И еще парочку. jQuery.event.props.push('pageX'); jQuery.event.props.push('pageY');  // Элементы для перетаскивания. $('.item')      // По клику устанавливаем/снимаем выделение, переключаем свойство draggable.     .on('click', function(e) {         e.preventDefault();         $(this).toggleClass('selected');         this.draggable = $(this).hasClass('selected');     })      // Перед тем как начать перетаскивать элементы,     .on('dragstart', function(e) {         var html = '',             // находим все выделенные элементы,             $selectedItems = $('.items .selected');          // собираем HTML выделенных элементов.         $selectedItems.each(function() {             html += this.outerHTML;         });          // Устанавливаем собранный HTML в качестве данных для перетаскивания.         // Это никак не влияет на визуальную часть.         e.dataTransfer.setData('text/html', html);          return true;     })      .on('dragend', function(e) {         resetUI();     });  // Дропзона $('.dropzone')      // При наведении добавляем класс dragover     .on('dragenter', function(e) {         $(this).addClass('dragover');     })      // Убираем класс dragover     .on('dragleave', function(e) {         $(this).removeClass('dragover');     })     .on('dragover', function(e) {         // Чтобы до элемента дошло событие drop, нужно запретить передачу по цепочке события dragover         if (e.preventDefault) e.preventDefault();         return false;     })      // Обрабатываем drop     .on('drop', function(e) {         // Достаем HTML из события         var html = e.dataTransfer.getData('text/html');          // Добавляем HTML к дропзоне         $(this).append(html);          resetUI();         return true;     });  function resetUI() {     $('.selected').removeClass('selected').attr('draggable', false);     $('.dragover').removeClass('dragover'); } 

Если вы попробовали выделить и перетащить несколько элементов, то заметили, что во время этого действия визуально перетаскивается только один. Сейчас мы это исправим!

По старинке это делается просто — подписываемся на события мыши и таскаем группу элементов за мышью. С приходом HTML5 от части этой рутины можно избавиться, переложив заботу об анимации на плечи браузера. Работать будет так же как и в случае с одним элементом.

Чтобы при перетаскивании нескольких элементов получить «правильную картинку» воспользуемся методом setDragImage объекта e.dataTransfer.

function setDragImage(image, x, y)
image — элемент, изображение которого будет использовано при перетаскивании.
x и y — смещение.

Итак, перед началом перетаскивания, после того как мы установили данные для перетаскивания e.dataTransfer.setData('text/html', html), нам нужно собрать правильный элемент-картинку для передачи в метод setDragImage. Сперва определим переменные, которые понадобятся для этого:

// Элемент, за который тащим. var $draggedItem = $(e.currentTarget),     draggedItemOffset = $draggedItem.offset(),      // Прямоугольник, в который вписываются выделенные элементы.     frame = getFrame($selectedItems),      // Координаты точки, за которую будем тащить.     dx = e.pageX - draggedItemOffset.left + (draggedItemOffset.left - frame.lx),     dy = e.pageY - draggedItemOffset.top + (draggedItemOffset.top - frame.ly),      // Элемент, который будем передавать как image в setDragImage.     $image = $(document.createElement('div')); 

Используя frame, установим нужный размер и координаты элементу-картинке:

$image.css({     position: 'absolute',     // Спрячем его подниз, чтобы не обрывал событие dragstart.     zIndex: -1,     left: frame.lx,     top: frame.ly,     width: Math.abs(frame.lx - frame.rx),     height: Math.abs(frame.ly - frame.ry) }); 

Добавим копии выделенных элементов к $image:

$selectedItems.each(function(i, item) {     var $item = $(item),         $clone = $item.clone(),         itemOffset = $item.offset();      // Позиционируем клоны внутри $image.     $clone.css({         position: 'absolute',         left: itemOffset.left - frame.lx,         top: itemOffset.top - frame.ly     });      $image.append($clone); }); 

Финальный аккорд:

// Добавляем $image на страницу. $('body').append($image);  // Устанавливаем $image в качестве картинки для перетаскивания. e.dataTransfer.setDragImage($image.get(0), dx, dy);  // Удаляем $image через 1 милисекунду. Если удалить сразу, // то вызов setDragImage произойдет до того как отрендерится $image. window.setTimeout(function() {     $image.remove(); }, 1); 

Метод getFrame используем для нахождения прямоугольника, в который вписываются выделенные элементы:

function getFrame($items) {     var offset = $items.first().offset(),         frame = { lx: offset.left, ly: offset.top, rx: offset.left, ry: offset.top };      $items.each(function() {         var $this = $(this),         offset = $this.offset(),         width = $this.width(),         height = $this.height();          if (offset.left < frame.lx) frame.lx = offset.left;         if (offset.top  < frame.ly) frame.ly = offset.top;         if (offset.left + width > frame.rx) frame.rx = offset.left + width;         if (offset.top + height > frame.ry) frame.ry = offset.top + height;      });     return frame; } 

Демо красивого перетаскивания
Код примеров на GitHub

ссылка на оригинал статьи http://habrahabr.ru/post/187590/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *