Skip to content

Instantly share code, notes, and snippets.

@Ibadichan
Last active March 12, 2021 09:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ibadichan/0d8784866e7ec5e0b3b98a2aad8905e9 to your computer and use it in GitHub Desktop.
Save Ibadichan/0d8784866e7ec5e0b3b98a2aad8905e9 to your computer and use it in GitHub Desktop.
The implementation of drag and drop.
class VanDragger {
constructor() {
this.draggableObject = {};
document.addEventListener('mousedown', this._mouseDown.bind(this));
document.addEventListener('mousemove', this._mouseMove.bind(this));
document.addEventListener('mouseup', this._mouseUp.bind(this));
}
onDragStart(draggableObject) {}
onDragEnd(draggableObject, droppableElement) {}
onDragCancel(draggableObject) {}
_mouseDown(event) {
const { target, which, pageX, pageY } = event;
const element = target.closest('.draggable');
if (which != 1 || !element) { return; }
event.preventDefault();
this.draggableObject.element = element;
this.draggableObject.downX = pageX;
this.draggableObject.downY = pageY;
}
_mouseMove(event) {
const { pageY, pageX } = event;
const { element, downY, downX } = this.draggableObject;
if (!element) { return; }
event.preventDefault();
if (!this.draggableObject.avatar) {
const moveX = pageX - downX;
const moveY = pageY - downY;
if (Math.abs(moveX) < 3 && Math.abs(moveY) < 3) { return; }
this.draggableObject.avatar = this._createAvatar();
if (!this.draggableObject.avatar) {
this.draggableObject = {};
return;
}
const coords = getCoords(this.draggableObject.avatar);
this.draggableObject.shiftX = downX - coords.left;
this.draggableObject.shiftY = downY - coords.top;
this._startDrag();
}
const { avatar, shiftY, shiftX } = this.draggableObject;
avatar.style.top = `${pageY - shiftY}px`;
avatar.style.left = `${pageX - shiftX}px`;
}
_mouseUp(event) {
if (this.draggableObject.avatar) {
this._finishDrag(event);
}
this.draggableObject = {};
}
_startDrag() {
const avatar = this.draggableObject.avatar;
document.body.appendChild(avatar);
avatar.classList.add('draggable-active');
this.onDragStart(this.draggableObject);
}
_finishDrag(event) {
const droppableElement = this._findDroppable(event);
if (droppableElement) {
this.onDragEnd(this.draggableObject, droppableElement);
} else {
this.onDragCancel(this.draggableObject);
}
}
_findDroppable(event) {
const { clientX, clientY } = event;
this.draggableObject.avatar.hidden = true;
const element = document.elementFromPoint(clientX, clientY);
this.draggableObject.avatar.hidden = false;
if (!element) { return null; }
return element.closest('.droppable');
}
_createAvatar() {
const avatar = this.draggableObject.element;
const avatarProps = {
parent: avatar.parentNode,
nextSibling: avatar.nextSibling
};
avatar.rollback = function() {
const { parent, nextSibling } = avatarProps;
parent.insertBefore(avatar, nextSibling);
avatar.classList.remove('draggable-active');
};
return avatar;
}
}
function getCoords(element) {
var box = element.getBoundingClientRect();
return {
top: box.top + pageYOffset,
left: box.left + pageXOffset
};
};
// Example of call
const dragger = new VanDragger();
dragger.onDragCancel = function(draggableObject) {
draggableObject.avatar.rollback();
};
dragger.onDragEnd = function(draggableObject, droppableElement) {
var avatar = draggableObject.avatar;
droppableElement.classList.remove('droppable-filled');
droppableElement.appendChild(avatar);
avatar.classList.remove('draggable-active');
avatar.classList.add('draggable-moved');
avatar.style.top = null;
avatar.style.left = null;
setTimeout(function() {
droppableElement.innerHTML = '';
droppableElement.classList.add('droppable-filled');
}, 1000);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment