On each mousemove, the draggable element positioning is getting reset to zero - javascript

I'm working on a component that moves a parts diagram around in a container. Right now everything works great on the first mousemove, but on the second the positioning styles are getting reset to zero.
I re-wrote the code outside of Vue and also made a codepen for your viewing.
Codepen: https://codepen.io/paytonburd/pen/WKqEjo
Code:
let diagram = document.getElementById('diagram')
let diagramImg = document.getElementById('diagram-image')
let startX;
let startY;
let walkX;
let walkY;
let dragging = false;
diagram.addEventListener('mousedown', (e) => {
dragging = true;
startX = e.pageX - diagram.offsetLeft;
startY = e.pageY - diagram.offsetTop;
})
diagram.addEventListener('mousemove', (e) => {
if (!dragging) return;
e.preventDefault();
let x = e.pageX - diagram.offsetLeft;
let y = e.pageY - diagram.offsetTop;
walkX = x - startX
walkY = y - startY
console.log(walkX, walkY)
diagramImg.style.top = walkY + 'px'
diagramImg.style.left = walkX + 'px'
})
diagram.addEventListener('mouseleave', () => {
dragging = false;
})
diagram.addEventListener('mouseup', () => {
dragging = false;
})

When you mouse down, you always set the startX and startY relative to the position of the diagram, which is always at 0, 0 and never moves.
I think what you want is to instead set them to relative to the current position of the diagram image instead:
let diagram = document.getElementById('diagram')
let diagramImg = document.getElementById('diagram-image')
let startX;
let startY;
let walkX;
let walkY;
let dragging = false;
diagram.addEventListener('mousedown', (e) => {
dragging = true;
//This is where it went wrong
startX = e.pageX - diagramImg.offsetLeft;
startY = e.pageY - diagramImg.offsetTop;
})
diagram.addEventListener('mousemove', (e) => {
if (!dragging) return;
e.preventDefault();
let x = e.pageX - diagram.offsetLeft;
let y = e.pageY - diagram.offsetTop;
walkX = x - startX
walkY = y - startY
console.log(walkX, walkY)
diagramImg.style.top = walkY + 'px'
diagramImg.style.left = walkX + 'px'
})
diagram.addEventListener('mouseleave', () => {
dragging = false;
})
diagram.addEventListener('mouseup', () => {
dragging = false;
})
https://codepen.io/anon/pen/yqdzyq?editors=1111

Related

Adding smooth scroll on my scrollable div

I have a div block containing the steps of one of my benefits that I made scrollable vertically by holding with the mouse ( Draggable Div )
I would like to add a smooth effect and not just a scroll that stops instantly when we stop the scroll.
What should I add to my code below?
Thank you and happy holidays! 🎅🏻🎄
const container = document.querySelector('.process-steps');
let startY;
let startX;
let scrollLeft;
let scrollTop;
let isDown;
container.addEventListener('mousedown',e => mouseIsDown(e));
container.addEventListener('mouseup',e => mouseUp(e))
container.addEventListener('mouseleave',e=>mouseLeave(e));
container.addEventListener('mousemove',e=>mouseMove(e));
function mouseIsDown(e){
isDown = true;
startY = e.pageY - container.offsetTop;
startX = e.pageX - container.offsetLeft;
scrollLeft = container.scrollLeft;
scrollTop = container.scrollTop;
}
function mouseUp(e){
isDown = false;
}
function mouseLeave(e){
isDown = false;
}
function mouseMove(e){
if(isDown){
e.preventDefault();
//Move vertcally
const y = e.pageY - container.offsetTop;
const walkY = y - startY;
container.scrollTop = scrollTop - walkY;
}
}
</script>

Drag multiple elements in JS

I'm new to JS so I need help to solve my problem :). I found a codepen that helped me drag one element of my website but the thing is that I would like to drag 4 elements separately. I applied the same class to all of them but it works only on the first one.
Link of the codepen : https://codepen.io/Coding-Artist/pen/zYWbYXV
I'm sure the solution is obvious to you (I would say a var or a for ?) but I'm learning and I really want to progress so if you could explain that would be perfect ! Thanks a lot
JS —
var draggableElem = document.querySelector(".draggable-elem");
let initialX = 0,
initialY = 0;
let moveElement = false;
//events object
let events = {
mouse: {
down: "mousedown",
move: "mousemove",
up: "mouseup"
},
touch: {
down: "touchstart",
move: "touchmove",
up: "touchend"
}
};
let deviceType = "";
//Detect touch device
const isTouchDevice = () => {
try {
//We try to create TouchEvent (it would fail for desktops and throw error)
document.createEvent("TouchEvent");
deviceType = "touch";
return true;
} catch (e) {
deviceType = "mouse";
return false;
}
};
isTouchDevice();
// start(mouse down/touch start)
draggableElem.addEventListener(events[deviceType].down, (e) => {
e.preventDefault();
//initial x and y points
initialX = !isTouchDevice() ? e.clientX : e.touches[0].clientX;
initialY = !isTouchDevice() ? e.clientY : e.touches[0].clientY;
// start movement
moveElement = true;
});
// Move
draggableElem.addEventListener(events[deviceType].move, (e) => {
//if movement==true then set top and left to new X and y while removing any offset
if (moveElement) {
e.preventDefault();
let newX = !isTouchDevice() ? e.clientX : e.touches[0].clientX;
let newY = !isTouchDevice() ? e.clientY : e.touches[0].clientY;
draggableElem.style.top = draggableElem.offsetTop - (initialY - newY) + "px";
draggableElem.style.left =
draggableElem.offsetLeft - (initialX - newX) + "px";
initialX = newX;
initialY = newY;
}
});
//mouse up/touch end
draggableElem.addEventListener(
events[deviceType].up,
(stopMovement = (e) => {
//stop movement
moveElement = false;
})
);
draggableElem.addEventListener("mouseleave", stopMovement);
document.addEventListener(events[deviceType].up, (e) => {
moveElement = false;
});
For it to work with multiple elements you should instantiate variables for each element and then add event listeners to them.
This can be done dynamically like in this codepen fork I made by using document.querySelectorAll and a for loop to iterate through the elements, instantiate variables, and add event listeners to each one.
My modified code (it's not perfect but it gets the job done):
let draggableElems = document.querySelectorAll("#draggable-elem");
let initialX = {},
initialY = {};
let moveElement = {};
//events object
let events = {
mouse: {
down: "mousedown",
move: "mousemove",
up: "mouseup"
},
touch: {
down: "touchstart",
move: "touchmove",
up: "touchend"
}
};
let deviceType = "";
//Detect touch device
const isTouchDevice = () => {
try {
//We try to create TouchEvent (it would fail for desktops and throw error)
document.createEvent("TouchEvent");
deviceType = "touch";
return true;
} catch (e) {
deviceType = "mouse";
return false;
}
};
isTouchDevice();
for (let i = 0; i < draggableElems.length; i++) {
var draggableElem = draggableElems[i];
// start(mouse down/touch start)
draggableElem.addEventListener(events[deviceType].down, (e) => {
e.preventDefault();
//initial x and y points
initialX[this] = !isTouchDevice() ? e.clientX : e.touches[0].clientX;
initialY[this] = !isTouchDevice() ? e.clientY : e.touches[0].clientY;
// start movement
moveElement[this] = true;
});
// Move
draggableElem.addEventListener(events[deviceType].move, (e) => {
//if movement==true then set top and left to new X and y while removing any offset
if (moveElement[this]) {
var elem = e.target;
e.preventDefault();
let newX = !isTouchDevice() ? e.clientX : e.touches[0].clientX;
let newY = !isTouchDevice() ? e.clientY : e.touches[0].clientY;
elem.style.top = elem.offsetTop - (initialY[this] - newY) + "px";
elem.style.left = elem.offsetLeft - (initialX[this] - newX) + "px";
initialX[this] = newX;
initialY[this] = newY;
}
});
//mouse up/touch end
draggableElem.addEventListener(
events[deviceType].up,
(stopMovement = (e) => {
//stop movement
moveElement[this] = false;
})
);
draggableElem.addEventListener("mouseleave", stopMovement);
document.addEventListener(events[deviceType].up, (e) => {
moveElement[this] = false;
});
}

allow native scroll in container during touchmove event like with drag and drop

Is there a way to allow native scrolling easily without heavy JS modifications when you reach the border of a div via custom drag and drop via touchmove listener?
When you drag the text in the div here you'll see the div inside is scrolling automatically
I provided an example with touchmove listeners but this one does not scroll, when you reach a border with your mouse
Is there an easy way to include a scrolling behavior to the 2nd example?
const element = document.body.querySelector('#draggable');
const isInContainer = (x,y) => {
const elements = document.elementsFromPoint(x, y)
return elements.find(el => el && el.classList && el.classList.contains('container')) || false;
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
}
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp)
}
element.addEventListener('mousedown', (e) => {
e.preventDefault();
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp)
});
In case someone has a native better solution I'm willing to accept that one... for the time being this would be my current way to solve the issue.
Note: I made a custom interval for scrolling and don't use the mousemove event so users don't have to move the mouse to trigger it. moving outside will start the interval moving inside will clear it.
// the container that should scroll
const scrollBody = document.getElementById('scrollContainer');
// parameter to check which directions should scroll
const scrollPositions = {
left: false,
right: false,
up: false,
down: false,
}
// how far should be scrolled
const nextScrollDistance = {
x: 0,
y: 0
}
// scroll interval
let scrollInterval= null;
const startScrolling = (scrollBody) => {
if (scrollInterval !== null) {
return true;
}
const intervalCallback = () => {
if (scrollInterval !== null && nextScrollDistance.x === 0 && nextScrollDistance.y === 0) {
window.clearInterval(scrollInterval);
scrollInterval = null;
} else {
scrollBody.scrollLeft += nextScrollDistance.x;
scrollBody.scrollTop += nextScrollDistance.y;
}
}
scrollInterval = window.setInterval(intervalCallback, 50);
}
const onMouseMove = (e) => {
if(isInContainer(e.pageX, e.pageY)){
element.style.top = e.pageY + 'px';
element.style.left = e.pageX + 'px';
}
const rects = scrollBody.getBoundingClientRect();
// check directions
// max x that can be scrolled
const maxX = scrollBody.scrollWidth - scrollBody.clientWidth;
// max y that can be scrolled
const maxY = scrollBody.scrollHeight - scrollBody.clientHeight;
// check all directions if it's even possible to scroll
const canScrollTop = Math.round(scrollBody.scrollTop) > 0;
const canScrollBottom = Math.round(scrollBody.scrollTop) < maxY;
const canScrollLeft = Math.round(scrollBody.scrollLeft) > 0;
const canScrollRight = Math.round(scrollBody.scrollLeft) < maxX;
// current x and y coordinates of the mouse
const x = e.pageX;
const y = e.pageY;
// dynamic value to decrease the speed.. otherwise it might scroll too fast
const minifier = 2;
// the modifiers for scrollTop and scrollLeft
nextScrollDistance.y = 0;
nextScrollDistance.x = 0;
if (canScrollBottom && y > rects.bottom) {
// distance between the right border and the mouse
const distance = Math.abs(y - rects.bottom);
// the next time it scrolls -> scroll distance / minifier
nextScrollDistance.y = Math.round(distance / minifier)
scrollPositions.down = true;
} else {
scrollPositions.down = false;
}
// all other directions...
if (canScrollTop && y < rects.top) {
const distance = Math.abs(y - rects.top);
nextScrollDistance.y = Math.round(distance / minifier) * -1;
scrollPositions.up = true;
} else {
scrollPositions.up = false;
}
if (canScrollRight && x > rects.right) {
const distance = Math.abs(x - rects.right);
nextScrollDistance.x = Math.round(distance / minifier)
scrollPositions.right = true;
} else {
scrollPositions.right = false;
}
if (canScrollLeft && x < rects.left) {
const distance = Math.abs(x - rects.left);
nextScrollDistance.x = Math.round(distance / minifier) * -1;
scrollPositions.left = true;
} else {
scrollPositions.left = false;
}
// in case one of those are set.. trigger scrolling
if (nextScrollDistance.x || nextScrollDistance.y) {
startScrolling();
}
}

Horizontal click and drag scroller for multiple sections help, works for only one

So I have a horizontal scrolling feature which works great in JS but only for one section with the classname, any other section that shares the same classname it doesn't replicate the same effects for. How can I make it so it works for every section that has the class name. You can see it in action on my website here where the quick links scrolls fine when you click but the other horizontally scrolling sections dont. Thank you - https://tutoryou.uixweb.dev/
const slider = document.querySelector('.scroller-div');
let mouseDown = false;
let startX, scrollLeft;
let startDragging = function (e) {
mouseDown = true;
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
};
let stopDragging = function (event) {
mouseDown = false;
};
slider.addEventListener('mousemove', (e) => {
e.preventDefault();
if(!mouseDown) { return; }
const x = e.pageX - slider.offsetLeft;
const scroll = x - startX;
slider.scrollLeft = scrollLeft - scroll;
});
// Add the event listeners
slider.addEventListener('mousedown', startDragging, false);
slider.addEventListener('mouseup', stopDragging, false);
slider.addEventListener('mouseleave', stopDragging, false);
for smooth scrolling (no jump effect) you have to create variables inside forEach()
const sliders = document.querySelectorAll('.scroller-div');
sliders.forEach(slider=>{
let startX, scrollLeft;
let startDragging = function(e) {
mouseDown = true;
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
};
let stopDragging = function(event) {
mouseDown = false;
};
slider.addEventListener('mousemove', (e)=>{
e.preventDefault();
if (!mouseDown) {
return;
}
const x = e.pageX - slider.offsetLeft;
const scroll = x - startX;
slider.scrollLeft = scrollLeft - scroll;
}
);
// Add the event listeners
slider.addEventListener('mousedown', startDragging, false);
slider.addEventListener('mouseup', stopDragging, false);
slider.addEventListener('mouseleave', stopDragging, false);
}
);
First change this:
const slider = document.querySelector('.scroller-div');
To this:
const sliders = document.querySelectorAll('.scroller-div');
querySelector will stop after finding the first element matching your selector while querySelectorAll will return a list of all the elements matching your selector.
Once you do that, you will need to loop through all the sliders and add the event listener to each one. So your code becomes wrapped in a forEach loop:
sliders.forEach(slider => {
let mouseDown = false;
let startX, scrollLeft;
let startDragging = function (e) {
mouseDown = true;
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
};
let stopDragging = function (event) {
mouseDown = false;
};
slider.addEventListener('mousemove', (e) => {
e.preventDefault();
if(!mouseDown) { return; }
const x = e.pageX - slider.offsetLeft;
const scroll = x - startX;
slider.scrollLeft = scrollLeft - scroll;
});
// Add the event listeners
slider.addEventListener('mousedown', startDragging, false);
slider.addEventListener('mouseup', stopDragging, false);
slider.addEventListener('mouseleave', stopDragging, false);
});

Can't update my mouse position, because of my horizontal scroll (LocomotiveScroll)

I'm trying to make a custom cursor with the Ink Cursor by Ricardo Mendieta. https://codepen.io/mendieta/pen/WgvENJ
The cursor is working, but the problem I have is that I use a horizontal scroll with Locomotive Scroll. When I scroll, the mouse position doesn't get updated. I tried to fix this with a mousewheel function. I can console log the mousewheel event, but it doesn't update my mouse position.
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mousewheel', onMouseScroll);
const onMouseMove = (event) => {
mousePosition.x = event.clientX - width / 2;
mousePosition.y = event.clientY - width / 2;
resetIdleTimer();
};
const onMouseScroll = (event) => {
console.log(event);
mousePosition.x = event.clientX - width / 2;
mousePosition.y = event.clientY - width / 2;
resetIdleTimer();
};
const render = (timestamp) => {
const delta = timestamp - lastFrame;
positionCursor(delta);
lastFrame = timestamp;
requestAnimationFrame(render);
};
const positionCursor = (delta) => {
let x = mousePosition.x;
let y = mousePosition.y;
dots.forEach((dot, index, dots) => {
let nextDot = dots[index + 1] || dots[0];
dot.x = x;
dot.y = y;
dot.draw(delta);
if (!idle || index <= sineDots) {
const dx = (nextDot.x - dot.x) * 0.35;
const dy = (nextDot.y - dot.y) * 0.35;
x += dx;
y += dy;
}
});
};
Is there a way I can update the mouse position when I scroll when the scroll direction is horizontal.
I just figured this out for my situation, which I think is similar to yours. Not sure if this will help or not - hopefully it does.
scroll.on('scroll', (instance) => {
let customCursor = document.querySelector(".customCursor");
let scrollPx = instance.scroll.x + "px";
customCursor.style.left = scrollPx;
});
So instead of trying to reconfigure where the mouse position is, I'm simply updating the "left" attribute of the custom cursor to be in sync with how much the horizontally laid out Locomotive scroll container is being scrolled.

Categories