195 lines
5.4 KiB
JavaScript
195 lines
5.4 KiB
JavaScript
const saveLocation = "input_item_position";
|
|
const defaultPosition = { x: 15, y: 400 };
|
|
|
|
//---Dragabble Helper Funcs--------------------------------------
|
|
|
|
// Calculate distance to nearest side.
|
|
function calcSideProximity (elementX, elementLength, winW) {
|
|
|
|
// Calc right short.
|
|
let short = winW - elementX - elementLength;
|
|
|
|
// See if it's left short.
|
|
if (elementX + 20 < winW / 2) {
|
|
short = elementX
|
|
}
|
|
|
|
return short
|
|
}
|
|
|
|
// Update elementX or elementY value on window resize
|
|
function calcPosition (elementPosition, elementLength, percent, short, win) {
|
|
|
|
// Is it close to the right/bottom side?
|
|
if (percent > 0.75) {
|
|
elementPosition = win - short - elementLength;
|
|
}
|
|
|
|
// Is it not close to a side?
|
|
if (percent < 0.75 && percent > 0.25) {
|
|
elementPosition = win * percent
|
|
}
|
|
|
|
return elementPosition
|
|
}
|
|
|
|
function draggify(elementId, parentId, margin) {
|
|
|
|
let element;
|
|
let parent;
|
|
|
|
/* only compatible with elements having equal width and height */
|
|
let elementLength;
|
|
|
|
// Cords of inputItem.
|
|
const elementX = Vue.ref();
|
|
const elementY = Vue.ref();
|
|
|
|
// Position of inputItem on terms of percentage of window.
|
|
let percentX;
|
|
let percentY;
|
|
|
|
// How close inputItem is to closest X or Y side.
|
|
let xShort;
|
|
let yShort;
|
|
|
|
//---Reposition Anime-----------------------------------------------
|
|
|
|
// Move element to target smoothly.
|
|
const repositionAnime = (xChange, yChange) => {
|
|
|
|
const xStep = xChange / 1000;
|
|
const yStep = yChange / 1000;
|
|
|
|
for (let i = 1; i <= 1000; i++) {
|
|
|
|
setTimeout(() => {
|
|
elementX.value += xStep;
|
|
elementY.value += yStep;
|
|
}, 30) // 60fps
|
|
|
|
}
|
|
}
|
|
|
|
//---Event Handlers-----------------------------------------------
|
|
|
|
// Update the position of inputItem on mouse dragging.
|
|
const onMouseMove = (e) => {
|
|
e.preventDefault();
|
|
|
|
elementX.value = element.offsetLeft + e.movementX;
|
|
elementY.value = element.offsetTop + e.movementY;
|
|
}
|
|
|
|
// Add an event listener for dragging.
|
|
const onMouseDown = (e) => {
|
|
e.preventDefault();
|
|
window.addEventListener('mousemove', onMouseMove);
|
|
window.addEventListener('mouseup', onMouseUp);
|
|
}
|
|
|
|
// Update position references when user is done moving targetEl.
|
|
const onMouseUp = (e) => {
|
|
e.preventDefault();
|
|
|
|
window.removeEventListener('mousemove', onMouseMove);
|
|
window.removeEventListener('mouseup', onMouseUp);
|
|
|
|
// See if and calculate reposition.
|
|
let x = 0; // vector change
|
|
let y = 0;
|
|
|
|
const winW = parent.clientWidth;
|
|
const winH = parent.clientHeight;
|
|
|
|
if (elementX.value < 0) {
|
|
x = (elementX.value - margin)*-1
|
|
}
|
|
|
|
if (elementX.value > winW - elementLength) {
|
|
const b = winW - elementLength - margin;
|
|
x = (elementX.value - b)*-1
|
|
}
|
|
|
|
// Reposition y
|
|
if (elementY.value < 0) {
|
|
y = (elementY.value - margin)*-1
|
|
}
|
|
|
|
if (elementY.value > winH - elementLength) {
|
|
const d = winH - elementLength - margin;
|
|
y = (elementY.value - d)*-1
|
|
}
|
|
|
|
// Reposition if needed.
|
|
if (x !== 0 || y !== 0) repositionAnime(x, y);
|
|
|
|
xShort = calcSideProximity(elementX.value, elementLength, winW);
|
|
yShort = calcSideProximity(elementY.value, elementLength, winH);
|
|
|
|
// Update percentages.
|
|
percentX = elementX.value / winW;
|
|
percentY = elementY.value / winH;
|
|
|
|
// Save position.
|
|
savePosition();
|
|
}
|
|
|
|
// Update position of targetEl on windowResize.
|
|
const onWindowResize = (_e) => {
|
|
|
|
elementX.value = calcPosition(elementX.value, elementLength, percentX, xShort, parent.clientWidth);
|
|
elementY.value = calcPosition(elementY.value, elementLength, percentY, yShort, parent.clientHeight);
|
|
|
|
savePosition();
|
|
}
|
|
|
|
const savePosition = () => {
|
|
window.localStorage.setItem(saveLocation, JSON.stringify({
|
|
x: elementX.value,
|
|
y: elementY.value
|
|
}));
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
Vue.onMounted(() => {
|
|
|
|
element = document.getElementById(elementId);
|
|
parent = document.getElementById(parentId);
|
|
|
|
elementLength = element.offsetWidth;
|
|
|
|
// Initialize the positional references.
|
|
percentX = elementX.value / parent.clientWidth;
|
|
percentY = elementY.value / parent.clientHeight;
|
|
|
|
xShort = calcSideProximity(elementX.value, elementLength, parent.clientWidth);
|
|
yShort = calcSideProximity(elementX.value, elementLength, parent.clientHeight);
|
|
|
|
// Then, we can listen for window resize (and mousedown).
|
|
element.addEventListener("mousedown", onMouseDown);
|
|
parent.addEventListener('resize', onWindowResize)
|
|
});
|
|
|
|
// remove event listeners on component dismount.
|
|
Vue.onUnmounted(() => {
|
|
element.removeEventListener('mousedown', onMouseDown);
|
|
parent.removeEventListener('resize', onWindowResize)
|
|
parent.removeEventListener('mouseup', onMouseUp)
|
|
parent.removeEventListener('mousemove', onMouseMove);
|
|
});
|
|
|
|
// Try loading initPosition, otherwise set default values
|
|
let initPosition;
|
|
const rawData = window.localStorage.getItem("saveLocation")
|
|
rawData ? initPosition = JSON.parse(rawData) : initPosition = defaultPosition;
|
|
|
|
elementX.value = initPosition.x;
|
|
elementY.value = initPosition.y;
|
|
|
|
return { elementX, elementY };
|
|
|
|
}
|
|
|
|
export default draggify;
|