Currently I'm engaged with implementing a Drag and Drop GUI. I've discovered that there a not many resources (tutorials, etc.) available. So I wrote this:
function Cursor(cssSelector, rightLimit, bottomLimit) {
var element = document.querySelector('#square');
var styles = window.getComputedStyle(square);
var x = 0;
var y = 0;
var fromLeft = 0;
var fromTop = 0;
var pushed = false;
var limits = {
top: 0,
right: rightLimit,
bottom: bottomLimit,
left: 0
}
// Uses the offsetX and the offsetY of the
// mousedown event.
this.setCoordinates = function(left, top) {
if (!fromLeft && !fromTop) {
fromLeft = left;
fromTop = top;
}
}
this.togglePushed = function() {
pushed ? pushed = false : pushed = true;
}
this.getPushed = function() {
return pushed;
}
// Uses the offsetX and the offsetY of the
// mousemove event.
this.moveCursor = function(offsetX, offsetY) {
// How much have the x and the y coordinate
// changed since the mousedown event?
var tmpX = offsetX - fromLeft;
var tmpY = offsetY - fromTop;
if ((x + tmpX) <= limits.right && (x + tmpX) >= limits.left &&
(y + tmpY) >= limits.top && (y + tmpY) <= limits.bottom) {
// If the values are valid then store them ...
x += tmpX;
y += tmpY;
// ... and use them to move the element.
element.style.left = x + 'px';
element.style.top = y + 'px';
}
}
}
var cursor = new Cursor('#square', 550, 450);
square.addEventListener('mousedown', function(ev) {
cursor.togglePushed();
cursor.setCoordinates(ev.offsetX, ev.offsetY);
});
document.body.addEventListener('mouseup', function(ev) {
cursor.togglePushed();
});
square.addEventListener('mousemove', function(ev) {
if (cursor.getPushed()) {
cursor.moveCursor(ev.offsetX, ev.offsetY);
}
});
body {
background-color: #eefafa;
}
#wrap {
width: 600px;
margin: 50px auto;
}
#panel {
height: 500px;
position: relative;
background-color: rgba(150, 150, 150, 0.2);
border-radius: 3px;
}
#square {
width: 50px;
height: 50px;
background-color: orangered;
border: 1px solid teal;
border-radius: 3px;
position: absolute;
top: 0px;
left: 0px;
}
.instruct {
font-size: 125%;
font-weight: bold;
font-family: helvetica;
}
<div id="wrap">
<p class="instruct">
Click the square. Keep the mouse-button pushed and
move the pointer slowly.
</p>
<div id="panel">
<div id="square"></div>
</div>
</div>
There's also a demo on CodePen.
2 Answers 2
Events
Instead of adding 3 (permanent) EventListener
, you could just keep the first one: square.addEventListener('mousedown'...
and when this is triggered, add the 2 other events mouseup
and mousemove
on the document (it will also enalbe the user to move quickly)
The mouseup
event should then simply remove the 2 event listeners (you will need to keep a trace of the EventListeners
).
You can then get rid off the pushed
variable.
The square.addEventListener
could be moved inside the constructor.
moveCursor
In the method moveCursor
instead of moving only if the new position is completely in the area, you could separately check the X and Y coordinates (It will then be easier to move near the edges).
Naming
I don't find the name Cursor
to be very expressive... maybe Draggable
is a better name?
-
\$\begingroup\$ "Draggable" is indeed a better name. Honestly: I had no good idea how to name it. Thanks a lot. \$\endgroup\$x32– x322015年12月14日 07:07:31 +00:00Commented Dec 14, 2015 at 7:07
One big thing I think you could do to improve this would be to separate out the css and javascript hooks in the HTML.
As they currently stand neither are re-usable without the other. If you want to create another draggable square you will automatically get the styles from the square selector. Or if you want to remove the square styles altogether, the element will not respond to the draggability added by javascript.
Here are a couple of ideas for html elements with attributes that define style and functionality more clearly:
<div class='draggable-element square'></div>
<div class='square' data-draggable='true'></div>
Both ways are good, with data attributes probably shading it because of how clearly they define functionality (draggable-element
could after-all be a style class). You can now switch these on and off in the HTML for either style or function reasons without worrying about side effects will happen elsewhere.
Another interesting thing to consider here is how clear it makes your HTML for other developers :-)
Another small point: IDs can't be re-used. I'd suggest removing them all and using classes instead for css. That way you can decorate whatever you like with the square styles.
Incidentally, you currently call Cursor
with the parameter #square
then hard-code it in the function. The parameter is better and should be used in the function.
pushed ? pushed = false : pushed = true;
can be simplified:pushed = !pushed;
\$\endgroup\$