I'm fairly new to JavaScript & JQuery, so apologies if I'm missing a trick or two.
I've figured out how to get JQuery UI draggable objects to use the grid option, and, once the page has loaded, "snap to an imaginary grid" which all draggable objects have reference to (explained in code comments). However, I can't figure out how to get this behaviour to occur .on("dragstart")
.
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type="text/javascript" src="dragger.js"></script>
</head>
<body>
<div id="parent">
<svg width="300" height="100" class="draggable" id="number1">
<rect x="0" y="0" rx="10" ry="10" width="300" height="100" style="fill:rgb(121,0,121);stroke-width:3;stroke:rgb(0,0,0);">
</svg>
<svg width="300" height="100" class="draggable" id="letterA">
<rect x="0" y="0" rx="100" ry="10" width="300" height="100" style="fill:rgb(0,121,121);stroke-width:3;stroke:rgb(255,0,0);">
</svg>
</div>
</body>
</html>
Note: There is a white gap between the two rectangles, via the second JavaScript below, this disappears once the block has been snapped to the grid. Alternatively, the 2 rectangles should be draggable onto each other and line up flush against one another to be considered snapped onto the grid.
JavaScript (dragger.js):
var roundedRemainder = function(numer, denom) {
return numer - (Math.round(numer / denom) * denom);
}
var snapPosition = function(obj, granularity) {
obj.position({
my: "left top", // Unchanging reference point on draggable object
at: "left top", // Unchanging reference point on reference object
of: "#parent", // The object that you want to move items with respect to.
using: function(position, data) {
var newPositions = {
// Get the difference between the "imaginary grid" and the current grid
left: function() {
return roundedRemainder(position.left, granularity);
},
top: function() {
return roundedRemainder(position.top, granularity);
}
}
// Move to the imaginary grid
$(this).css(newPositions);
return newPositions;
}
});
}
$(function() {
var gridSize = 50;
$(".draggable")
// Enable grid usage
.draggable({
grid: [gridSize, gridSize]
})
.on("dragstart", function(event, ui) {
var newPos = snapPosition(ui.helper, gridSize);
})
});
Proof the code in snapPosition
works:
var roundedRemainder = function(numer, denom) {
return numer - (Math.round(numer / denom) * denom);
}
$(function() {
var gridSize = 50;
$(".draggable")
// Enable grid usage
.draggable({
grid: [gridSize, gridSize]
})
.position({
my: "left top", // Unchanging reference point on draggable object
at: "left top", // Unchanging reference point on reference object
of: "#parent", // The object that you want to move items with respect to.
using: function(position, data) {
var newPositions = {
// Get the difference between the "imaginary grid" and the current grid
left: function() {
return roundedRemainder(position.left, granularity);
},
top: function() {
return roundedRemainder(position.top, granularity);
}
}
// Move to the imaginary grid
$(this).css(newPositions);
}
})
});
The first JavaScript is trying to change the position of the block once dragging starts, to snap it to the imaginary grid. The second does this automatically upon loading of the page, but never again. If I were to change the imaginary grids granularity from 50 to 79, for instance, dragging would not bring the objects back onto the grid as desired.
Is there somewhere I could look to learn how to do this? Is it doable?
To clarify:
I've already been through Google, but either terms akin to "start", "drag" and "position" aren't unique enough to narrow things down, or I haven't found the right place. I've also scoured through the JQuery (UI) archives.
Many thanks in advance!
Okay, so turns out there were a few examples of ignorance on my behalf. I'll go through them below to help others as well, but if you're just after a solution, then look no further:
var roundedRemainder = function(numer, denom) {
if (denom > 1) // Only calculate when there is a chance the draggable isn't on the grid.
return numer - (Math.round(numer / denom) * denom); // Note: denom = 0 is invalid.
else
return 0; // If denom is invalid, or 1, then assume the draggable is on the grid.
}
$(function() {
var gridSize = 79;
var _this = this;
$(".draggable")
.draggable({
// Enable grid constraints
grid: [gridSize, gridSize],
// At start of dragging (aka, only do once at the beginning)
// snap the draggable object onto its parents grid.
drag: function(event, ui) {
var gridOffsetLeft;
var gridOffsetTop;
ui.helper.position({
my: "left top", // For the top left of the draggable object
at: "left top", // Link to the top left of the reference object
of: $(this).parent(), // Make the reference object the parent of the draggable object
// Calculate the grid offset from where the object ORIGINATED from
using: function(position, data) {
gridOffsetLeft = roundedRemainder(position.left, gridSize);
gridOffsetTop = roundedRemainder(position.top, gridSize);
}
});
// Calculate the total offset based off of the current
// location of the draggable object, and the previously
// calculated offset.
gridOffsetLeft -= roundedRemainder(ui.position.left, gridSize);
gridOffsetTop -= roundedRemainder(ui.position.top, gridSize);
// Apply offsets & hence snap the draggable onto the
// parents grid.
ui.position.left += gridOffsetLeft;
ui.position.top += gridOffsetTop;
}
})
});
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type="text/javascript" src="dragger.js"></script>
</head>
<body>
<div id="parent">
<svg width="300" height="100" class="draggable" id="number1">
<rect x="0" y="0" rx="10" ry="10" width="300" height="100" style="fill:rgb(121,0,121);stroke-width:3;stroke:rgb(0,0,0);">
</svg>
<svg width="300" height="100" class="draggable" id="letterA">
<rect x="0" y="0" rx="100" ry="10" width="300" height="100" style="fill:rgb(0,121,121);stroke-width:3;stroke:rgb(255,0,0);">
</svg>
</div>
</body>
</html>
Bugs:
then
over. I've not really understood this yet which is why it isn't in the solution, however I did read (and lost the link) a stack overflow which mentioned using var _then = then;
, or using a binding method. If I find the link I'll edit the answer.start:
or .on("dragstart", ...)
functionality, as per the documentation. This lead into a preventDefault
issue whereby either the start command was ignored, hence no snapping, but I could drag, or if I used event.preventDefault
at the beginning of start, it would snap but no longer drag. Turns out that the drag:
functionality only runs at the beginning of the drag once (please correct me if I'm wrong). By putting the snapping function back in there, another bug was solved.(To be clear, there are 3 reference locations, the parent, the original location of the dragged object, and the current location of the dragged object).