Mouse dragging an element - javascript

I've been waiting to ask this question for a long time, but couldn't, because I knew I would get bad reputation. This post was very hard to post, but I REALLY need this code...
Let's say there was a draggable element with the ID of "dragme"... You have to drag and drop the element to a specific spot. I was wondering if there is a code that does this task automatically for me when I execute a function. Lets name that function "dropElement". I am trying to drag "dragme" to my mouse position with a "dragElement" function with jquery or js.
This is what I tried:
(function() {
'use strict';
var mouseX = 0;
var mouseY = 0;
var timer = 0;
//tracks mouse position
document.body.addEventListener("mousemove", function(e) {mouseX = e.clientX; mouseY = e.clientY;});
function dropElement() {
$("#dragme").trigger($.Event("mousedown", {button: 0}));
$("body").trigger($.Event("mouseup", {button: 0, clientX: mouseX, clientY: mouseY}));
timer = setTimeout(drop, 100);
}
dropElement() //executes function and drops "dragme" to mouse position

I found the code in the question a bit complex to follow, especially with a timing function.
Instead I've gone back to basics (and vanilla JS) to think about the sequence of events. The user moves the mouse, we aren't interested unless they have put the mousedown within the element we want to drag. So this snippet sets a variable isDown which is set to true when the user puts the mouse down on the element.
Then it looks for a mousemove event on the whole window and if isDown is set it moves the element.
We also look for the mouseup event on the window and unset isDown.
The reason for looking for some events on the actual element and some on the window is because things are moving - the mouse may get out of the window before it is released for example.
let isDown = false;
const dragMe = document.querySelector('.dragme');
dragMe.addEventListener('mousedown', function() {
isDown = true;
});
window.addEventListener('mouseup', function() {
isDown = false;
});
window.addEventListener('mousemove', function() {
if (isDown) {
dragMe.style.top = event.clientY + 'px';
dragMe.style.left = event.clientX + 'px';
}
});
.dragme {
width: 5em;
height: 5em;
background-color: cyan;
position: relative;
top: 0;
left: 0;
}
<div class="dragme">Drag me</div>

I hope this sample helps you
var drag = {
elem: null,
x: 0,
y: 0,
state: false
};
var delta = {
x: 0,
y: 0
};
function dropElement(e){
var cur_offset = $("#autoDrag").offset();
$("#autoDrag").animate({
left: (e.pageX),
top: (e.pageY )
});
}
$(document).mousedown(function(e) {
dropElement(e);
})
$("#dragMe").mousedown(function(e) {
drag.elem = dragMe;
drag.x = e.pageX;
drag.y = e.pageY;
drag.state = true;
})
$(document).mousemove(function(e) {
if ( drag.state) {
delta.x = e.pageX - drag.x;
delta.y = e.pageY - drag.y;
var cur_offset = $(drag.elem).offset();
$(drag.elem).offset({
left: (cur_offset.left + delta.x),
top: (cur_offset.top + delta.y)
});
drag.x = e.pageX;
drag.y = e.pageY;
}
})
$("#dragMe").mouseup(function() {
drag.state = false;
})
#dragMe {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: 80px;
height: 80px;
padding:10px;
background-color: #00a1ff;
color: white;
border-radius: 50px;
}
#autoDrag {
position: absolute;
right:0;
display: flex;
justify-content: center;
align-items: center;
text-align:center;
width: 80px;
height: 80px;
padding:10px;
background-color: #ff00ff;
color: white;
border-radius: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span id="dragMe">DragMe!</span>
<span id="autoDrag">Click somewhere I will be there!</span>

Related

Trying to get this smoother and more natural in behavior

My implementation,
http://kodhus.com/kodnest/land/PpNFTgp
I am curious, as I am not able for some reason to figure this out, how to get my JavaScript to make my slider behave more natural and smoother, if someone knows, how to, or can make this, feel free. I'd be happy to understand.
JavaScript:
const thumb = document.querySelector('.thumb');
const thumbIndicator = document.querySelector('.thumb .thumb-indicator');
const sliderContainer = document.querySelector('.slider-container');
const trackProgress = document.querySelector('.track-progress');
const sliderContainerStart = sliderContainer.offsetLeft;
const sliderContainerWidth = sliderContainer.offsetWidth;
var translate;
var dragging = false;
var percentage = 14;
document.addEventListener('mousedown', function(e) {
if (e.target.classList.contains('thumb-indicator')) {
dragging = true;
thumbIndicator.classList.add('focus');
}
});
document.addEventListener('mousemove', function(e) {
if (dragging) {
console.log('moving', e)
if (e.clientX < sliderContainerStart) {
translate = 0;
} else if (e.clientX > sliderContainerWidth + sliderContainerStart) {
translate = sliderContainerWidth;
} else {
translate = e.clientX - sliderContainer.offsetLeft;
}
thumb.style.transform = 'translate(-50%) translate(' + translate + 'px)';
trackProgress.style.transform = 'scaleX(' + translate / sliderContainerWidth + ')'
}
});
function setPercentage() {
thumb.style.transform = 'translate(-50%) translate(' + percentage/100 * sliderContainerWidth + 'px)';
trackProgress.style.transform = 'scaleX(' + percentage/100 + ')';
}
function init() {
setPercentage();
}
init();
document.addEventListener('mouseup', function(e) {
dragging = false;
thumbIndicator.classList.remove('focus');
});
EDIT: Is there a way to smoothly and naturally increment by one for every slow move?
Is it possible to make to behave as if, like when one clicks the progress bar so that it jumps there?
The kodhus site is very janky in my browser, so I can't tell if your code lacks responsiveness or whether it's the site itself. I feel that your code is a bit convoluted: translate and width / height are mixed unnecessarily; no need to use a dragging boolean when that information is always stored in the classlist. The following slider performs nicely, and has a few considerations I don't see in yours:
stopPropagation when clicking the .thumb element
drag stops if window loses focus
pointer-events: none; applied to every part of the slider but the .thumb element
let applySliderFeel = (slider, valueChangeCallback=()=>{}) => {
// Now `thumb`, `bar` and `slider` are the elements that concern us
let [ thumb, bar ] = [ '.thumb', '.bar' ].map(v => slider.querySelector(v));
let changed = amt => {
thumb.style.left = `${amt * 100}%`;
bar.style.width = `${amt * 100}%`;
valueChangeCallback(amt);
};
// Pressing down on `thumb` activates dragging
thumb.addEventListener('mousedown', evt => {
thumb.classList.add('active');
evt.preventDefault();
evt.stopPropagation();
});
// Releasing the mouse button (anywhere) deactivates dragging
document.addEventListener('mouseup', evt => thumb.classList.remove('active'));
// If the window loses focus dragging also stops - this can be a very
// nice quality of life improvement!
window.addEventListener('blur', evt => thumb.classList.remove('active'));
// Now we have to act when the mouse moves...
document.addEventListener('mousemove', evt => {
// If the drag isn't active do nothing!
if (!thumb.classList.contains('active')) return;
// Compute `xRelSlider`, which is the mouse position relative to the
// left side of the slider bar. Note that *client*X is compatible with
// getBounding*Client*Rect, and using these two values we can quickly
// get the relative x position.
let { width, left } = slider.getBoundingClientRect();
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Clamp `xRelSlider` between 0 and the slider's width
if (xRelSlider < 0) xRelSlider = 0;
if (xRelSlider > width) xRelSlider = width;
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
slider.addEventListener('mousedown', evt => {
let { width, left } = slider.getBoundingClientRect();
// Clicking the slider also activates a drag
thumb.classList.add('active');
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
changed(0);
};
let valElem = document.querySelector('.value');
applySliderFeel(document.querySelector('.slider'), amt => valElem.innerHTML = amt.toFixed(3));
.slider {
position: absolute;
width: 80%; height: 4px; background-color: rgba(0, 0, 0, 0.3);
left: 10%; top: 50%; margin-top: -2px;
}
.slider > .bar {
position: absolute;
left: 0; top: 0; width: 0; height: 100%;
background-color: #000;
pointer-events: none;
}
.slider > .thumb {
position: absolute;
width: 20px; height: 20px; background-color: #000; border-radius: 100%;
left: 0; top: 50%; margin-top: -10px;
}
.slider > .thumb.active {
box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.5);
}
<div class="slider">
<div class="bar"></div>
<div class="thumb"></div>
</div>
<div class="value"></div>

element flickering when it gets dragged

Hello guys i have following problem:
Whenever i drag an element it flickers and it looks very annoying. I couldnt find the root of the problem.
Here is my code snippet:
moveElement(element, e) {
let clientX = e.clientX;
let clientY = e.clientY;
let offsetX = e.offsetX;
let offsetY = e.offsetY;
let height = element.getBoundingClientRect().height;
let width = element.getBoundingClientRect().width;
window.requestAnimationFrame(function() {
element.style.setProperty("left", clientX - (width - offsetX) + "px");
element.style.setProperty("top", clientY - (height - offsetY) + "px");
});
}
Here is he full code:
class Dragger{
constructor() {
this.drags = [];
this.drops = [];
this.mover = null;
this.collectDragAndDrop();
}
dragItem(element) {
element.style.setProperty("position", "fixed");
this.mover = this.moveElement.bind(null, element);
element.addEventListener("mousemove", this.mover);
}
moveElement(element, e) {
let clientX = e.clientX;
let clientY = e.clientY;
let offsetX = e.offsetX;
let offsetY = e.offsetY;
let height = element.getBoundingClientRect().height;
let width = element.getBoundingClientRect().width;
window.requestAnimationFrame(function() {
element.style.setProperty("left", clientX - (width - offsetX) + "px");
element.style.setProperty("top", clientY - (height - offsetY) + "px");
});
}
dropItem(element) {
element.removeEventListener("mousemove", this.mover);
}
collectDragAndDrop() {
document.querySelectorAll("[drag]").forEach(element => {
let name = element.attributes.drag.nodeValue;
let findDup = this.drags.some(el => el.name === name);
if (findDup) throw Error("Duplicated drag attribute: " + name);
this.drags.push({
element,
name
});
element.addEventListener("mousedown", this.dragItem.bind(this, element));
element.addEventListener("mouseup", this.dropItem.bind(this, element));
});
}
}
new Dragger();
.box1 {
background: black;
width: 100px;
height: 100px;
color: white;
}
.box2 {
background: red;
width: 100px;
height: 100px;
position: fixed;
right: 100px;
}
<div class="box1" drag="test"></div>
<div class="box2" drag="test2"></div>
Can somebody tell me why this flickers so much?
Your math is wonky. You're only accounting for the current mouse position instead of calculating the amount of movement that has occurred. The only reason your boxes are moving at all is because the function is waiting for the animation frame so there is some change in those coordinates while it waits.
You should also consider that if the mouseup occurs while the mouse is no longer over the element, the element won't get the event and thus will continue dragging when you mouse back over it. It's better to set a flag that keeps track of the mouse state.
var isMouseDown = false;
addEventListener("mousedown", ()=>isMouseDown = true);
addEventListener("mouseup", ()=>isMouseDown = false);
document.querySelectorAll("[drag]").forEach(element=>{
element.addEventListener("mousemove", e=>{
if(!isMouseDown) return;
requestAnimationFrame(function() {
var rect = element.getBoundingClientRect();
element.style.left = rect.x + e.movementX + "px";
element.style.top = rect.y + e.movementY + "px";
});
});
});
.box1 {
background: black;
width: 100px;
height: 100px;
color: white;
position: absolute
}
.box2 {
background: red;
width: 100px;
height: 100px;
position: fixed;
right: 100px;
}
<div class="box1" drag="test"></div>
<div class="box2" drag="test2"></div>

Style drag ghost element

I am facing an issue, where I am dragging a div.
While the ghost element looks good on MacOs(yes both on Chrome and FireFox),it appears to be too transparent,in Windows (yes both on Chrome and FireFox.
I tried multiple approaches but nothing seems to work.
So is it possible to style the ghost element?
Also, I tried to make an image of that element on the go, and use it as ghost dragging image, but the transparency issue still remains.
You can do this by implementing dragging of the element yourself in JavaScript. That way, you can apply a CSS class to the element while it is being dragged, which styles it in any way you wish.
const d = 'dragging';
const container = document.getElementById('container');
const el = document.getElementById('drag');
var cOffX = 0;
var cOffY = 0;
el.addEventListener('mousedown', dragStart);
function dragStart(e) {
e = e || window.event;
e.preventDefault();
cOffX = e.clientX - el.offsetLeft;
cOffY = e.clientY - el.offsetTop;
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
el.classList.add(d);
container.style.cursor = 'move';
};
function dragMove(e) {
e = e || window.event;
e.preventDefault();
el.style.top = (e.clientY - cOffY).toString() + 'px';
el.style.left = (e.clientX - cOffX).toString() + 'px';
};
function dragEnd(e) {
e = e || window.event;
e.preventDefault();
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
el.classList.remove(d);
container.style.cursor = null;
};
#container {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
#drag {
position: absolute;
height: 100px;
width: 100px;
background-color: lime;
border-radius: 0;
transition: background-color 0.25s, border-radius 0.25s;
cursor: move;
}
#drag.dragging {
background-color: navy;
border-radius: 50%;
}
<div id="container">
<div id="drag"></div>
</div>
As others have said, the ghosting is browser-based and can't be changed, so it seems you need your own solution if you want to get around that.

javascript game - joystick issue

Note: I've asked this question again because I was not be able to edit my old question. (No idea if this was a SO bug or a bug with my beta safari.)
So I want to generate a joystick, as it is used in many games. The joystick stands out of a background and a movable billet. The billet may only be moved within the background.
Here you can find both images
let background = new Image()
let stick = new Image()
let enableMaxDistance = false
background.onload = function() {
$(this).css({
position: "absolute",
left: "2%",
bottom: "2%",
width: "30%"
}).appendTo(document.body)
}
stick.onload = function() {
$(this).css({
position: "absolute",
left: "2%",
bottom: "2%",
width: "30%"
}).appendTo(document.body)
let zeroPosition = $(this).offset()
$(this).draggable({
drag: function(e, ui) {
let distance = Math.sqrt(Math.pow(zeroPosition.top - $(this).offset().top, 2) + Math.pow(zeroPosition.left - $(this).offset().left, 2));
if (distance > 60 && enableMaxDistance) {
e.preventDefault();
}
},
scroll: false
})
}
background.src = "https://i.stack.imgur.com/5My6q.png"
stick.src = "https://i.stack.imgur.com/YEoJ4.png"
html, body {
overflow: hidden;
height: 100%;
}
input {
margin-top: 10%;
margin-left: 50%;
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<input onclick="enableMaxDistance = !enableMaxDistance " value="toggle maximum distance" type="button"/>
But while implementing this joystick some problems occurred:
My idea was to make the stick maneuverable by using jqueryUI and to calculate its distance to the origin with each drag event. If the distance is too large, the event will be stopped (not executed) using e.preventDefault();. --> If the distance in the frame, the stick is wearable.
The problem is that ...
The stick is no longer draggable after moving out the maximum distance.
The stick should be just be movable inside the bounds without canceling the event so that I have to grab the stick again and again if I'm touching the bounds (going out of the maximum distance).
How to implement a working joystick using jQuery + jQueryUI?
The issue with your logic is that as soon as the drag event is prevented the distance value will be over 60 due to the inherent delays in JS processing time. Therefore the logic in the next drag is immediately cancelled as the distance > 60 check is immediately hit. While it would be possible to fix this, a much better solution would be to not allow the value to ever be greater than the limit you set.
To achieve this I would not recommend using jQueryUI. You can do it quite easily using native methods which give you more direct control of the positioning without having to fight against any built in logic.
It's also slightly more performant, which is vital when dealing with game mechanics; especially when dealing with direct user input which needs to be as responsive as possible.
With that said, you can use modify the basic logic as laid out in Twisty's comment on this question. Then it simply becomes a question of changing the size of the relevant elements, which is a trivial task. Try this:
var $canvas = $('#background');
var $pointer = $('#stick');
var $window = $(window);
var settings = {
width: $canvas.prop('offsetWidth'),
height: $canvas.prop('offsetHeight'),
top: $canvas.prop('offsetTop'),
left: $canvas.prop('offsetLeft')
};
settings.center = [settings.left + settings.width / 2, settings.top + settings.height / 2];
settings.radius = settings.width / 2;
let mousedown = false;
$window.on('mouseup', function() { mousedown = false; });
$pointer.on('mousedown', function() { mousedown = true; });
$pointer.on('mouseup', function() { mousedown = false; });
$pointer.on('dragstart', function(e) { e.preventDefault(); });
$window.on('mousemove', function(e) {
if (!mousedown)
return;
var result = limit(e.clientX, e.clientY);
$pointer.css('left', result.x + 'px');
$pointer.css('top', result.y + 'px');
});
function limit(x, y) {
var dist = distance([x, y], settings.center);
if (dist <= settings.radius) {
return {
x: x,
y: y
};
} else {
x = x - settings.center[0];
y = y - settings.center[1];
var radians = Math.atan2(y, x)
return {
x: Math.cos(radians) * settings.radius + settings.center[0],
y: Math.sin(radians) * settings.radius + settings.center[1]
}
}
}
function distance(dot1, dot2) {
var x1 = dot1[0],
y1 = dot1[1],
x2 = dot2[0],
y2 = dot2[1];
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
html,
body {
height: 100%;
}
#background {
background-image: url('https://i.stack.imgur.com/5My6q.png');
position: absolute;
height: 200px;
width: 200px;
top: 50%;
left: 50%;
margin: -100px 0 0 -100px;
border-radius: 200px;
border: dashed #ccc 1px;
}
#stick {
background: transparent url('https://i.stack.imgur.com/YEoJ4.png') 50% 50%;
position: absolute;
width: 100px;
height: 100px;
border-radius: 100px;
margin: -50px 0 0 -50px;
top: 50%;
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="background"></div>
<div id="stick"></div>

How to make mouseup fire anyehere?

I have a drag and drop script that is relatively functional. However, I want to be able to trigger mouseup anywhere on the screen. Is there a way to trigger mouseup outside of the window, or outside of the current element? I know this is possible and I've seen other questions like this. I wanted to find a way in vanilla Javascript to detect mouseups like this.
document.onmousemove = mouseCoords;
var x = 0;
var y = 0;
var cl1= false;
var divid;
var offs1;
var offs2;
var topPos;
var leftPos;
function mouseCoords(e) {
x = e.x
y = e.y
if(cl1 === true){
document.getElementById(divid).style.top = topPos + (y-offs1) + 'px';
document.getElementById(divid).style.left = leftPos + (x-offs2) + 'px';
}
}
var drag = function(i, cas) {
divid= i
switch(cas){
case 1:
var rect = document.getElementById(divid).getBoundingClientRect();
leftPos = rect.left;
topPos = rect.top;
offs1 = y;
offs2 = x;
cl1= true;
break;
case 0:
offs1 = 0;
offs2 = 0;
cl1= false;
break;
}
}
#block{
width: 100px; z-index: 20; height: 50px; background-color: blue; position: fixed; user-select: none; -webkit-user-select: none;
}
.drag{
width: 200px; height: 100px; background-color: red; position: fixed;
}
<div id="block">mouseup doesn't trigger over me!</div>
<div id="1" class="drag" onmousedown="drag(1, 1)" onmouseup="drag(1, 0)"></div>
Use
document.addEventListener("mouseup", drag(null, 0));
for mouseup,
and this code for mousedown.
document.addEventListener("mousedown", drag(null, 1));
Basically, document.addEventListener works for the whole window. "mouseup" tells the script that the event is a mouseup, and the final bit is the function to be executed (drag(1, 0))

Categories

Resources