I'm trying to implement drag events in d3 (v6.2), and I'm having some trouble interpreting the d3 x and y coordinates. Consider the following code. When I inspect the console output, it seems to me like, in the drag handler:
event.x and event.y are the SUM of the user (graph) coordinate object locations and the total movement/distance in SVG coordinates?
event.dx and event.dy are indicator that tell you whether since the last update, you've moved left/up (-1), not moved (0), or right/down (1)?
event.subject.x and event.subject.y give the location of the object being dragged?
if I want the current coordinates of the drag (either in user/graph coordinates or SVG coordinates), I need to calculate them myself (see example in code, which seems to work)?
I can't find where the specifics of this are documented. My questions are:
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
<style>
circle.circ {
fill: red;
stroke: black;
stroke-width: 1px;
}
</style>
<script>
function dragHandler(e, d){
const objx = scaleX.invert(e.x - d.x + scaleX(d.x))
const objy = scaleY.invert(e.y - d.y + scaleY(d.y))
console.log(`x: ${e.x}, dx: ${e.dx} sx: ${e.subject.x} objx: ${objx}\ny: ${e.y} dy: ${e.dy} sy: ${e.subject.y} objy: ${objy}`)
}
var drag = d3
.drag()
.on('drag', dragHandler)
var x = [{x: 150, y: 150}]
var svg = d3.select("svg")
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
}
var width = svg.attr("width") - margin.left - margin.right
var height = svg.attr("height") - margin.top - margin.bottom
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")")
var scaleX = d3.scaleLinear().range([0, width]).domain([140, 160])
var scaleY = d3.scaleLinear().range([height, 0]).domain([140, 160])
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(scaleX));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(scaleY))
var circ = g.selectAll(".circ")
.data(x)
.enter()
.append("circle")
.attr("r", 5)
.attr("cx", function(d) { return scaleX(d.x) })
.attr("cy", function(d) { return scaleY(d.y) })
.attr("class", "circ")
.call(drag);
</script>
</body>
<html>
Your impression is right. You can either
event.x/y and event.subject.x/yevent.clickDistance() as a getterThese two work in different coordinate systems.
mousemove event.What you did was the in-between way that normally is not needed: by applying the scaleX/Y transform, you computed the coordinates in the system of the SVG viewBox, i. e. the coordinate system that is found when the viewBox is applied to the outermost <svg> element. That can be the client coordinate system, but only if the attribute is not set, or the width and height values of the element match the contents of the viewBox.
In almost every practical use case, the two will differ. The "Scalable" in SVG is just about that distinction. Leaving out the viewBox like you did deprives you of the possibility to easily scale your grafic as a whole.
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