Перетаскиванием объектов на 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
Автор: hoodoy