Obviously I did something janky with the math on the mouse cursor and element offset, but I can't figure out what. Why does the element jump around so much? The goal is to be able to drag the element, but I need it to stay relative to the mouse (IE not SNAP to the cursor). Also, I actually want to be able to drag using the parent element, if that makes sense, as reflected in the code. You can "drag" on the parent container to move the target.
Also, I realize there are other issues that need improvement (like dragging off the page, etc), but the purpose of this question is to fix the jumping back and forth.
let scaleX = 1;
let scaleY = 1;
let skewX = 0;
let skewY = 0;
let translateX = 0;
let translateY = 0;
let isPanning = false;
let $map = $('#map');
let $container = $('#mapCanvas');
function moveMap() {
$("#map").css({
transform: ` matrix(${scaleX}, ${skewX}, ${skewY}, ${scaleY}, ${translateX}, ${translateY})`
});
}
$container.on("mousedown", function(event) {
isPanning = true;
});
$container.on("mouseup", function(event) {
isPanning = false;
});
$container.on("mousemove", function(event) {
if (isPanning) {
translateX = event.pageX + (event.pageX - $map.offset().left) ;
translateY = event.pageY + (event.pageY - $map.offset().top);
moveMap();
}
});
#mapCanvas {
position: absolute;
left: 0;right:0;top:0;bottom:0;
user-select: none;
}
#map {
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
border: 1px solid black;
color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="mapCanvas">
<div id="map">DRAG ME</div>
</div>
I'm sure you looked at the snippet first... If not, well do it now!
So what was wrong with your code?
mousemove
is a "machinegun" event.
It fires like hell! So after the event fired, you had to update the global variables and then call the moveMap
function... Meanwhile, mousemove
already fired again... Totally messing the calculation. A millisecond is a decenia for a browser (I hardly exaggerate...)
To avoid that timing problems, just pass the event to your function. Or just have the functions straight in the event handler. Why do you need a distinct named function anyway?
Second thing... You have to know where the mouse was, relatively to the draggable element, to have the desired smooth dragging effect. The position of the element used in the calculation is the top-left corner. But maybe the mouse was near the bottom-right corner on "dragStart"... You have to know. And that only can be deducted from the mousedown
event.
Enjoy dragging!
let scaleX = 1;
let scaleY = 1;
let skewX = 0;
let skewY = 0;
//let translateX = 0; // No need to be global
//let translateY = 0;
let isPanning = false;
// Additional object to store the mouse "offset" versus the element position
let mouse_vs_element;
let $map = $('#map');
let $container = $('#mapCanvas');
function moveMap(event) {
let translateX = event.pageX - mouse_vs_element.X
let translateY = event.pageY - mouse_vs_element.Y
//console.log(translateX, translateY)
$("#map").css({
transform: ` matrix(${scaleX}, ${skewX}, ${skewY}, ${scaleY}, ${translateX}, ${translateY})`
});
}
$container.on("mousedown", function(event) {
isPanning = true;
// Get the draggable element position at start
let element_pos = event.target.getBoundingClientRect()
// Mouse position relative to that element
mouse_vs_element = {
X: event.pageX - element_pos.left,
Y: event.pageY - element_pos.top
}
});
$container.on("mouseup", function(event) {
isPanning = false;
// Clear that (optionnal in fact, but a good practice)
mouse_vs_element = {}
});
$container.on("mousemove", function(event) {
if (isPanning) {
moveMap(event);
}
});
#mapCanvas {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
user-select: none;
}
#map {
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
border: 1px solid black;
color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="mapCanvas">
<div id="map">DRAG ME</div>
</div>