На странице есть набор элементов типа А, которые можно по одному или группой перетащить в элемент типа Б. Пользователь должен видеть, перетаскивает он один элемент или несколько. Перетаскивать один элемент просто, достаточно присвоить свойству 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; }
ссылка на оригинал статьи http://habrahabr.ru/post/187590/
Добавить комментарий