157 lines
No EOL
4.7 KiB
JavaScript
157 lines
No EOL
4.7 KiB
JavaScript
let DragManager = new function() {
|
||
|
||
/**
|
||
* составной объект для хранения информации о переносе:
|
||
* {
|
||
* elem - элемент, на котором была зажата мышь
|
||
* avatar - аватар
|
||
* downX/downY - координаты, на которых был mousedown
|
||
* shiftX/shiftY - относительный сдвиг курсора от угла элемента
|
||
* }
|
||
*/
|
||
let dragObject = {};
|
||
|
||
let self = this;
|
||
|
||
function onMouseDown(e) {
|
||
|
||
if (e.which != 1) return;
|
||
|
||
let elem = e.target.closest('.draggable');
|
||
if (!elem) return;
|
||
|
||
dragObject.elem = elem;
|
||
|
||
// запомним, что элемент нажат на текущих координатах pageX/pageY
|
||
dragObject.downX = e.pageX;
|
||
dragObject.downY = e.pageY;
|
||
|
||
return false;
|
||
}
|
||
|
||
function onMouseMove(e) {
|
||
if (!dragObject.elem) return; // элемент не зажат
|
||
|
||
if (!dragObject.avatar) { // если перенос не начат...
|
||
let moveX = e.pageX - dragObject.downX;
|
||
let moveY = e.pageY - dragObject.downY;
|
||
|
||
// если мышь передвинулась в нажатом состоянии недостаточно далеко
|
||
if (Math.abs(moveX) < 3 && Math.abs(moveY) < 3) {
|
||
return;
|
||
}
|
||
|
||
// начинаем перенос
|
||
dragObject.avatar = createAvatar(e); // создать аватар
|
||
if (!dragObject.avatar) { // отмена переноса, нельзя "захватить" за эту часть элемента
|
||
dragObject = {};
|
||
return;
|
||
}
|
||
|
||
// аватар создан успешно
|
||
// создать вспомогательные свойства shiftX/shiftY
|
||
let coords = getCoords(dragObject.avatar);
|
||
dragObject.shiftX = dragObject.downX - coords.left;
|
||
dragObject.shiftY = dragObject.downY - coords.top;
|
||
|
||
startDrag(e); // отобразить начало переноса
|
||
}
|
||
|
||
// отобразить перенос объекта при каждом движении мыши
|
||
dragObject.avatar.style.left = e.pageX - dragObject.shiftX + 'px';
|
||
dragObject.avatar.style.top = e.pageY - dragObject.shiftY + 'px';
|
||
|
||
return false;
|
||
}
|
||
|
||
function onMouseUp(e) {
|
||
if (dragObject.avatar) { // если перенос идет
|
||
finishDrag(e);
|
||
}
|
||
|
||
// перенос либо не начинался, либо завершился
|
||
// в любом случае очистим "состояние переноса" dragObject
|
||
dragObject = {};
|
||
}
|
||
|
||
function finishDrag(e) {
|
||
let dropElem = findDroppable(e);
|
||
|
||
if (!dropElem) {
|
||
self.onDragCancel(dragObject);
|
||
} else {
|
||
self.onDragEnd(dragObject, dropElem);
|
||
}
|
||
}
|
||
|
||
function createAvatar(e) {
|
||
|
||
// запомнить старые свойства, чтобы вернуться к ним при отмене переноса
|
||
let avatar = dragObject.elem;
|
||
let old = {
|
||
parent: avatar.parentNode,
|
||
nextSibling: avatar.nextSibling,
|
||
position: avatar.position || '',
|
||
left: avatar.left || '',
|
||
top: avatar.top || '',
|
||
zIndex: avatar.zIndex || ''
|
||
};
|
||
|
||
// функция для отмены переноса
|
||
avatar.rollback = function() {
|
||
old.parent.insertBefore(avatar, old.nextSibling);
|
||
avatar.style.position = old.position;
|
||
avatar.style.left = old.left;
|
||
avatar.style.top = old.top;
|
||
avatar.style.zIndex = old.zIndex
|
||
};
|
||
|
||
return avatar;
|
||
}
|
||
|
||
function startDrag(e) {
|
||
let avatar = dragObject.avatar;
|
||
|
||
// инициировать начало переноса
|
||
document.body.appendChild(avatar);
|
||
avatar.style.zIndex = 9999;
|
||
avatar.style.position = 'absolute';
|
||
}
|
||
|
||
function findDroppable(event) {
|
||
// спрячем переносимый элемент
|
||
dragObject.avatar.hidden = true;
|
||
|
||
// получить самый вложенный элемент под курсором мыши
|
||
let elem = document.elementFromPoint(event.clientX, event.clientY);
|
||
|
||
// показать переносимый элемент обратно
|
||
dragObject.avatar.hidden = false;
|
||
|
||
if (elem == null) {
|
||
// такое возможно, если курсор мыши "вылетел" за границу окна
|
||
return null;
|
||
}
|
||
|
||
return elem.closest('.droppable');
|
||
}
|
||
|
||
document.onmousemove = onMouseMove;
|
||
document.onmouseup = onMouseUp;
|
||
document.onmousedown = onMouseDown;
|
||
|
||
this.onDragEnd = function(dragObject, dropElem) {};
|
||
this.onDragCancel = function(dragObject) {};
|
||
|
||
};
|
||
|
||
|
||
function getCoords(elem) { // кроме IE8-
|
||
let box = elem.getBoundingClientRect();
|
||
|
||
return {
|
||
top: box.top + pageYOffset,
|
||
left: box.left + pageXOffset
|
||
};
|
||
|
||
} |