Issue
I'm trying to create draggable elements that can also be resized by dragging the bottom right corner with the help of the resize: both
CSS rule.
So far, I can drag it around the screen, but I can't figure out how to detect if the cursor is in the "resize" state, because attempting to resize the element moves it instead.
Currently, as a workaround, I'm simply checking if the cursor is within 20px of the bottom right corner, but that's not very accurate (and probably browser/OS dependent), and in some places it refuses to resize or move the element at all.
Any suggestions?
!function(){
"use strict";
let x, y, drag;
document.addEventListener("mousedown", function(e) {
if (e.target.parentNode.lastChild !== e.target && e.target.parentNode.classList.contains("main")) {
//bring element to the front and dispatch mousedown event again otherwise resize doesn't work
e.target.parentNode.appendChild(e.target);
return e.target.dispatchEvent(new MouseEvent(e.type, e));
}
if (!e.target.classList.contains("draggable"))
return;
/* if cursor within 20px from bottom right corner don't move */
const r = e.target.getBoundingClientRect();
if (r.right < e.x + 20 && r.bottom < e.y + 20)
return;
drag = e.target;
x = e.x - drag.offsetLeft;
y = e.y - drag.offsetTop;
document.body.classList.add("drag");
drag.classList.add("drag");
});
document.addEventListener("mouseup", function(e) {
document.body.classList.remove("drag");
drag = drag && drag.classList.remove("drag");
});
document.addEventListener("mousemove", function(e) {
if (!drag || e.x - drag.offsetLeft == x || e.y - drag.offsetTop == y)
return;
drag.style.left = (e.x - x) + "px";
drag.style.top = (e.y - y) + "px";
});
/*init*/
for (let i = 0, c, d = document.getElementsByClassName("main")[0].children; i < d.length; i++) {
c = (0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6);
d[i].style.backgroundColor = '#' + c;
d[i].classList.toggle("dark", ((parseInt(c.substr(0, 2), 16) * 299) + (parseInt(c.substr(2, 2), 16) * 587) + (parseInt(c.substr(4, 2), 16) * 114)) / 1000 < 128);
d[i].style.left = document.documentElement.scrollWidth / 8 + Math.random() * (document.documentElement.scrollWidth / 1.33 - d[i].offsetWidth) + "px";
d[i].style.top = document.documentElement.scrollHeight / 8 + Math.random() * (document.documentElement.scrollHeight / 1.33 - d[i].offsetHeight) + "px";
}
}()
div.main>div {
width: 5em;
height: 5em;
border: 1px solid black;
position: absolute;
resize: both;
overflow: hidden;
mix-blend-mode: hard-light;
display: flex;
border-radius: 0.3em;
}
div.main>div:hover {
box-shadow: 0 0 5px black;
}
div.main>div:last-child {
box-shadow: 0 0 10px black;
}
div.draggable {
cursor: grab;
}
div.main>div:not(.draggable):before {
content: "can't move me";
}
div.main>div.draggable:before {
content: "move me";
}
div.main>div:before {
color: black;
text-shadow: 0 0 1em black;
margin: auto;
text-align: center;
}
div.main>div.dark:before {
color: white;
text-shadow: 0 0 1em white;
}
body.drag {
user-select: none;
}
body.drag div.draggable {
cursor: grabbing;
}
<div class="main">
<div></div>
<div class="draggable"></div>
<div class="draggable"></div>
<div class="draggable"></div>
</div>
Solution
You could wrap the mouse-target (.draggable
) inside a resizeable container element.
This way, the UI for the CSS-resize will get hit before the draggable element and you can handle only the dragging nicely:
!function(){
"use strict";
let x, y, drag;
document.addEventListener("mousedown", function(e) {
if (e.target.parentNode.lastChild !== e.target && e.target.parentNode.classList.contains("main")) {
//bring element to the front and dispatch mousedown event again otherwise resize doesn't work
e.target.parentNode.appendChild(e.target);
return e.target.dispatchEvent(new MouseEvent(e.type, e));
}
if (!e.target.classList.contains("draggable"))
return;
e.preventDefault();
drag = e.target.parentNode;
x = e.x - drag.offsetLeft;
y = e.y - drag.offsetTop;
document.body.classList.add("drag");
drag.classList.add("drag");
});
document.addEventListener("mouseup", function(e) {
document.body.classList.remove("drag");
drag = drag && drag.classList.remove("drag");
});
document.addEventListener("mousemove", function(e) {
if (!drag || e.x - drag.offsetLeft == x || e.y - drag.offsetTop == y)
return;
drag.style.left = (e.x - x) + "px";
drag.style.top = (e.y - y) + "px";
});
/*init*/
for (let i = 0, c, d = document.getElementsByClassName("main")[0].children; i < d.length; i++) {
c = (0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6);
d[i].style.backgroundColor = '#' + c;
d[i].classList.toggle("dark", ((parseInt(c.substr(0, 2), 16) * 299) + (parseInt(c.substr(2, 2), 16) * 587) + (parseInt(c.substr(4, 2), 16) * 114)) / 1000 < 128);
d[i].style.left = document.documentElement.scrollWidth / 8 + Math.random() * (document.documentElement.scrollWidth / 1.33 - d[i].offsetWidth) + "px";
d[i].style.top = document.documentElement.scrollHeight / 8 + Math.random() * (document.documentElement.scrollHeight / 1.33 - d[i].offsetHeight) + "px";
}
}()
.resizeable {
width: 5em;
height: 5em;
border: 1px solid black;
position: absolute;
resize: both;
overflow: hidden;
mix-blend-mode: hard-light;
border-radius: 0.3em;
}
div.main>div:hover {
box-shadow: 0 0 5px black;
}
div.main>div:last-child {
box-shadow: 0 0 10px black;
}
div.draggable {
cursor: grab;
width: 100%;
height: 100%;
display: flex;
}
div.no-drag {
display: flex;
}
div.main div.no-drag:before {
content: "can't move me";
}
div.draggable:before {
content: "move me";
}
div.main div:before {
color: black;
text-shadow: 0 0 1em black;
margin: auto;
text-align: center;
}
div.main>div.dark:before {
color: white;
text-shadow: 0 0 1em white;
}
body.drag {
user-select: none;
}
body.drag div.draggable {
cursor: grabbing;
}
<div class="main">
<div class="resizeable no-drag"></div>
<div class="resizeable"><div class="draggable"></div></div>
<div class="resizeable"><div class="draggable"></div></div>
<div class="resizeable"><div class="draggable"></div></div>
</div>
Answered By - Kaiido
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.