Skip to content

Instantly share code, notes, and snippets.

@kyleparisi
Last active January 2, 2019 13:21
Show Gist options
  • Save kyleparisi/6aaf592026ec90bf511b7ac71264736f to your computer and use it in GitHub Desktop.
Save kyleparisi/6aaf592026ec90bf511b7ac71264736f to your computer and use it in GitHub Desktop.
A single script to handle dragging of html or svg elements
function coroutine(f) {
var o = f(); // instantiate the coroutine
o.next(); // execute until the first yield
return function(x) {
o.next(x);
}
}
var loop = coroutine(function*() {
var e;
while (e = yield) {
if (e.type == 'mousedown') {
while (e = yield) {
if (e.type == 'mousemove')
move(e);
if (e.type == 'mouseup')
break;
}
}
// ignore mousemoves
}
});
$('#box').mousedown(loop);
$(window).mousemove(loop)
.mouseup(loop);
try {
if (document) {
}
} catch (e) {
throw new Error("Drag module only works in browsers");
}
const Drag = function(view) {
let documentation = `
# Usage
\`Drag([HTMLElement] view).in([HTMLElement] document.body)\` -> draghandler
For side affect changes:
draghandler
.onDrag([Function] fn)
`;
if (!view) {
console.warn("No view defined for drag handler");
return;
}
let handler = {
get: (state, key) => {
if (key === "endDrag") {
delete state.drag;
if (state.dragging) {
delete state.dragging;
}
return state;
}
if (key === "state") {
return state;
}
return state[key];
},
set: function(state, property, value) {
if (property === "mouse") state.mouse = value;
let valid = [true];
if (property === "dragging") {
state.dragging = value;
// if dragging is not an array, make it an array
if (!(state.dragging instanceof Array))
state.dragging = new Array(state.dragging);
// validate all the entries
valid = state.dragging.map(currentValue => {
if (
!(currentValue instanceof HTMLElement) &&
!(currentValue instanceof SVGElement)
) {
console.error(
"Dragging property is not an HTMLElement",
currentValue
);
return false;
}
});
// establish all the initial positions for the drag
state.drag = state.dragging.map(currentValue => {
return {
startX: view.offsetLeft,
startY: view.offsetTop,
startMouseX: state.mouse.clientX,
startMouseY: state.mouse.clientY
};
});
}
// conduct modifications of elements
if (property === "mouse" && state.dragging && valid[0]) {
state.dragging.map((currentValue, index) => {
// amount traveled
state.drag[index].dx = value.clientX - state.drag[index].startMouseX;
state.drag[index].dy = value.clientY - state.drag[index].startMouseY;
state.left = state.drag[index].startX + state.drag[index].dx;
state.styleLeft = state.left + "px";
state.right =
state.left + Math.round(currentValue.getBoundingClientRect().width);
state.styleRight = state.right + "px";
state.top = state.drag[index].startY + state.drag[index].dy;
state.styleTop = state.top + "px";
});
window.getSelection().removeAllRanges();
state.onDrag();
}
return state;
}
};
let cbs = [];
let state = {
documentation: documentation,
in: function(boundry) {
boundry.addEventListener("mousemove", e => (proxy.mouse = e));
return this;
},
onDrag: function(cb) {
if (cb) {
cbs.push(cb);
return false;
}
cbs.map(callback => {
callback(state);
});
}
};
let proxy = new Proxy(state, handler);
view.onmousedown = e => {
proxy.dragging = e.target;
};
view.onmouseup = () => proxy.endDrag;
return proxy;
};
// allow to be used as common js module for browserify, etc.
try {
module.exports = Drag;
} catch (e) {}
try {
if (document) {}
} catch(e) {
throw new Error('Drag module only works in browsers')
}
var Drag = function (view) {
let documentation =
`
# Usage
\`Drag([HTMLElement] view).in([HTMLElement] document.body)\` -> draghandler
For side affect changes:
draghandler
.onDrag([Function] fn)
`;
if (!view) {
console.warn('No view defined for drag handler');
return
}
let handler = {
get: (state, key) => {
if (key === 'endDrag') {
delete state.drag;
if (state.dragging) {
var els = document.querySelectorAll('.dragging');
[].forEach.call(els, function(el) {
el.className = el.className.replace('dragging', 'draggable');
});
delete state.dragging
}
}
if (key === 'state') {
return state
}
return state[key]
},
set: function (state, property, value) {
if (property === 'mouse') state.mouse = value;
var valid = [true];
if (property === 'dragging') {
state.dragging = value;
// if dragging is not an array, make it an array
if (!(state.dragging instanceof Array)) state.dragging = new Array(state.dragging);
// validate all the entries
valid = state.dragging.map((currentValue) => {
if (!(currentValue instanceof HTMLElement) &&
!(currentValue instanceof SVGElement)) {
console.error('Dragging property is not an HTMLElement', currentValue);
return false;
}
if ((!currentValue.style || !currentValue.style.position) &&
!(currentValue instanceof SVGElement)) {
console.error('Element needs to have style position: fixed', currentValue)
return false;
}
});
// establish all the initial positions for the drag
state.drag = state.dragging.map((currentValue) => {
return {
startX: currentValue.offsetLeft,
startY: currentValue.offsetTop,
startMouseX: state.mouse.clientX,
startMouseY: state.mouse.clientY
}
})
}
// conduct modifications of elements
if (property === 'mouse' && state.dragging && valid[0]) {
state.dragging.map((currentValue, index) => {
state.drag[index].dx = value.clientX - state.drag[index].startMouseX;
state.drag[index].dy = value.clientY - state.drag[index].startMouseY;
state.left = state.drag[index].startX + state.drag[index].dx;
state.right = state.left + Math.round(currentValue.getBoundingClientRect().width);
currentValue.style.left = state.left + 'px';
state.top = state.drag[index].startY + state.drag[index].dy;
currentValue.style.top = state.top + 'px';
currentValue.className = currentValue.className.replace('draggable', 'dragging');
});
state.onDrag()
}
return state
}
};
let cbs = [];
let state = {
documentation: documentation,
in: function (boundry) {
boundry.addEventListener('mousemove', e => proxy.mouse = e);
return this
},
onDrag: function (cb) {
if (cb) cbs.push(cb);
cbs.map(callback => {
callback(state)
})
}
};
let proxy = new Proxy(state, handler);
view.onmousedown = e => {
if (e.target.classList[0] === 'draggable') {
proxy.dragging = e.target;
}
};
view.onmouseup = () => proxy.endDrag;
return proxy
};
// allow to be used as common js module for browserify, etc.
try {
module.exports = Drag
} catch (e) {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment