Reputation: 892
I am working on a simple D3 test and I can't get my nodes to drag for some reason. Referring to this example: (http://bl.ocks.org/mbostock/1804919) what I want is for each of my nodes to start and stay at their starting position but like in the example I want the nodes to be dragable and when they are released to return back to their starting place. What am I missing?
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.Properties{
fill: green;
stroke: black;
stroke-width: 2px;
}
svg {
background-color: #FFF;
cursor: default;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var width = 960, height = 500, colors = d3.scale.category10();
var svg = null, force = null;
var circle = null, path = null;
var nodes = null, links = null;
var nodesArray = null, linkArray = null;
var count = 0;
var element = "body"; var numEdges = 2, numNodes = 5;
var i = 0; var L = 16, r = 12, lineLimit = 10;
var d = 2 * r + L;
var R = (count - 1) * d;
var m = width / 2;
var X;
var drag = d3.behavior.drag();
svg = d3.selectAll(element).append('svg').attr('width', width).attr('height', height);
nodes = d3.range(numNodes).map(function () {
X = m - (R / 2) + (i * d);
++i;
return {
x: X,
y: (height) / 3,
fx: X,
fy: height / 3,
id: i-1,
reflexive: true
};
});
for (var i = 0; i < numNodes; ++i) {
d3.select(element).append("h3").text("Node " + i + ": " + nodes[i].id);
}
i = -1;
links = d3.range(numEdges).map(function () {
i++;
return {
//
source: nodes[i],
target: nodes[i+1],
left: false,
right: true
}
});
for (var i = 0; i < numEdges; ++i) {
d3.select(element).append("h3").text("Source: " + links[i].source.id + " Target: " + links[i].target.id);
}
force = d3.layout.force().size([width, height]).nodes(nodes).links(links).linkDistance(40).linkStrength(0.1).charge(-300);
linkArray = svg.selectAll('.link').data(links).enter().append('line').attr('class', 'link')
.attr('x1', function (d) {
return nodes[d.source.id].x;
})
.attr('y1', function (d) { return nodes[d.source.id].y; })
.attr('x2', function (d) { return nodes[d.target.id].x; })
.attr('y2', function (d) { return nodes[d.target.id].y; });
nodeArray = svg.selectAll("circle").data(nodes).enter().append('circle').attr('class', "Properties").attr('r', 12)
.attr('cx', function (d) { return d.x })
.attr('cy', function (d) { return d.y })
.style('cursor', 'pointer').call(drag);
force.on('tick', tick);
force.start();
//Move Nodes towards focus
function gravity(alpha) {
return function (d) {
d.y += (d.fy - d.y) * alpha;
d.x += (d.fx - d.x) * alpha;
};
}
function tick() {
alert("in Stepforce" + nodeArray[1].id);
nodeArray
.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; })
.each(gravity(.2 * e.alpha));
linkArray.attr('x1', function (d) {
return d.source.x;
})
.attr('y1', function (d) { return d.source.y; })
.attr('x2', function (d) { return d.target.x; })
.attr('y2', function (d) { return d.target.y; });
}
</script>
Upvotes: 0
Views: 197
Reputation: 3709
You're missing the chaining of drag events to your drag
variable. You have to call functions for the dragstart
, drag
, and dragend
events.
In your specific case, you want to (1) record the initial position of the circle, (2) translate the circle based on the location of the mouse, and (3) return the circle back to its original position when the drag ends.
For example:
var drag = d3.behavior.drag()
.on('dragstart', dragstart) // when the drag starts, call dragstart()
.on('drag', drag) // during drag, call drag()
.on('dragend', dragend); // call dragend() on release
var originalPosition;
function dragstart() {
// store starting coordinates
originalPosition = d3.mouse(this); // returns array with x and y coords -> [x, y]
}
function drag() {
var m = d3.mouse(this);
// make the circle's coordinates match those of the mouse
d3.select(this)
.attr('cx', m[0])
.attr('cy', m[1]);
}
function dragend() {
// send circle back to original position
d3.select(this)
.attr('cx', originalPosition[0])
.attr('cy', originalPosition[1]);
}
You can also add transition()
before changing the attributes in dragend()
to animate them back to the starting position. Additionally, you might want to send the circles back to their original coordinates, rather than the location of the mouse when the drag started. This should get you started, though.
Upvotes: 1