In the snippet below I have div
, when I first start draging the div around it jumps slightly to the right, making me think that it recalculates it's initial starting position to wherever the mouse is located rather than adding to the original position.
I've tried finding other examples using non-svg elements but could not find any and the documentation for d3-drag does not say anything about not being able to properly drag around non-svg elements, not sure what I'm missing.
Here's the code:
var box = d3.select("body").append("div")
.attr("class", "box")
d3.select(".box").call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
console.log(d3.event.x)
d3.select(this)
.style("left", d3.event.x + "px")
.style("top", d3.event.y + "px")
}
function dragended(d, i) {
d3.select(this).classed("active", false);
}
body {
padding-top: 25px;
margin: auto;
width: 600px;
}
div.box {
position: relative;
width: auto;
padding: 20px 20px 20px 20px;
margin: 5px 5px 5px 5px;
background: crimson;
}
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
This comes down to the drag subject.
The drag in your example doesn't know what the drag should be relative to, this means the drag doesn't factor in where on the box you mouse down on (d3.event.x will be mouse x position in this case regardless of where you mouse down). Whenever you finish a drag the top left of the box is positioned according the drag event x,y. You can see the effect of this if you initiate a drag on the top left the drift is less worse than if you initiate a drag on the bottom right of the box.
Let's take advantage of the default drag subject function in d3-drag:
function subject(d) {
return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
In the question's example we are not providing a datum, so d
is null
and d3.event.x
and d3.event.y
are used to calculate the reference point for the drag.
Instead, let's give our subject a datum so we can calculate the relative movement of the drag to the box and update the datum to reflect the results of any drag events.
Update the appending of the box:
var box = d3.select("body").append("div")
.attr("class", "box")
.datum({x:0,y:0}); // starting position
And update the drag function:
function dragged(d) {
d3.select(this)
.style("left", (d.x = d3.event.x) + "px")
.style("top", (d.y = d3.event.y) + "px")
}
Now we have a datum that is updated whenever a drag is conducted, and the box is positioned based on the relative movement that is applied to that datum:
var box = d3.select("body").append("div")
.attr("class", "box")
.datum({x:0,y:0});
d3.select(".box").call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this)
.style("left", (d.x = d3.event.x) + "px")
.style("top", (d.y = d3.event.y) + "px")
}
function dragended(d, i) {
d3.select(this).classed("active", false);
}
body {
/* padding-top: 25px;
margin: auto;*/
width: 600px;
}
div.box {
position: relative;
width:40px;
padding: 20px 20px 20px 20px;
margin: 5px 5px 5px 5px;
height: 40px;
background: crimson;
}
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
The default functionality of d3.drag anticipates that the drag datum has x and y properties for positioning.
A bit of a deeper explanation (but for simplicity, assuming drag events only consist of a start and end):
What happens with no drag subject is we have (using x values here):
d3.event.x = subject.x + mouseUp.x - mouseDown.x
Mouse down/up being drag start/end, and mouseUp.x - mouseDown.x
being the change in x
over the duration of the drag
With no subject datum, this simplifies to:
d3.event.x = mouseUp.x
as subject.x
is set to mouseDown.x
when the drag starts:
function subject(d) {
return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
But by setting the subject datum to some initial value representing the top left corner we get:
d3.event.x = topLeft.x + mouseUp.x - mouseDown.x
Now we have change in x plus topLeft.x
, exactly what we want to position the box relative to mouse movement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With