Two things I need are that you could choose the speed of the scroll when dragging and upon release it would still move a bit, not stop instantly.
codepen: https://codepen.io/rKaiser/pen/qGomdR
I can set the speed close enough. Reasonable to add some momentum? or perhaps there is a more fitting plugin to use here? Thanks.
const slider = document.querySelector('.container');
let isDown = false;
let startX;
let scrollLeft;
slider.addEventListener('mousedown', (e) => {
isDown = true;
slider.classList.add('active');
startX = e.pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
});
slider.addEventListener('mouseleave', () => {
isDown = false;
slider.classList.remove('active');
});
slider.addEventListener('mouseup', () => {
isDown = false;
slider.classList.remove('active');
});
slider.addEventListener('mousemove', (e) => {
if(!isDown) return;
e.preventDefault();
const x = e.pageX - slider.offsetLeft;
const walk = (x - startX) * 0.3; //scroll-speed
slider.scrollLeft = scrollLeft - walk;
});
You need to increase scrollLeft with certain amount with every frame, after mouseup, till speed becomes 0. Something like=>
slider.addEventListener('mouseup', () => {
isDown = false;
slider.classList.remove('active');
speed = (-scrollLeft+slider.scrollLeft)/(new Date()-t)*500; //t is time of mouse down start
const draw=()=>{
if(speed>0){
slider.scrollLeft = slider.scrollLeft + speed--; //increase by certain amount
}else{
momentum=false;
}
requestAnimationFrame(draw);
}
momentum=true;
draw();
});
slider.addEventListener('mousemove', (e) => {
if(!isDown && !momentum){
return;
}..
Related
I’ve been struggling to solve this for days now. There is a slider with clickable items at the bottom of the page. I’ve made the slider draggable. But the issue is that when the dragging stops the click action also happens on the item on which mousedown event happened. I’ve tried to adding ‘pointer-vents: none’ to the inner item after dragging starts, but in this case draggint doesn’t work at all. Any ideas on how can I solve this problem?
https://duolutions-wondrous-site.webflow.io/straudo-branding-naming
Read-Only link of webflow: https://preview.webflow.com/preview/duolutions-wondrous-site?utm_medium=preview_link&utm_source=designer&utm_content=duolutions-wondrous-site&preview=9e5312190b70be978f65b8da04fbcefc&pageId=6377be41955fe23fb7c4bf33&workflow=preview
<script>
let isDown = false;
let startX;
let scrollLeft;
const slider = document.querySelector('.items');
const end = () => {
isDown = false;
slider.classList.remove('active');
}
const start = (e) => {
isDown = true;
slider.classList.add('active');
startX = e.pageX || e.touches[0].pageX - slider.offsetLeft;
scrollLeft = slider.scrollLeft;
}
const move = (e) => {
if(!isDown) return;
e.preventDefault();
const x = e.pageX || e.touches[0].pageX - slider.offsetLeft;
const dist = (x - startX);
slider.scrollLeft = scrollLeft - dist;
}
(() => {
slider.addEventListener('mousedown', start);
slider.addEventListener('touchstart', start);
slider.addEventListener('mousemove', move);
slider.addEventListener('touchmove', move);
slider.addEventListener('mouseleave', end);
slider.addEventListener('mouseup', end);
slider.addEventListener('touchend', end);
})();
</script>
There is no link to a working sandbox, so I can't be sure it will help, but I've noticed you didn't use Event.stopPropagation(). This should stop the propagation of the current event. You can read more about it here
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>
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;
});
}
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);
});
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