Use a larger transparent shape as an alternative to intersection or collision detection when dragging a node. Sparked by this StackOverflow question
Created
May 18, 2013 04:31
-
-
Save explunit/5603250 to your computer and use it in GitHub Desktop.
D3.js tree with "drag near" logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<html> | |
<head> | |
<title>Simple Tree Demo</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
.nodelink { | |
fill: none; | |
stroke: #ccc; | |
stroke-width: 4.5px; | |
} | |
.templink { | |
fill: none; | |
stroke: red; | |
stroke-width: 3px; | |
} | |
</style> | |
</head> | |
<body> | |
Drag the lonely circle near the others | |
<div id="viz"></div> | |
<script type="text/javascript"> | |
var treeData = {"name" : "A", "children" : [ {"name" : "A1" }, {"name" : "A2" }, {"name" : "A3" } ] }; | |
var selectedNode = null; | |
var draggingNode = null; | |
// ------------- moving ------------------------------- | |
var overCircle = function(d) { | |
selectedNode = d; | |
updateTempConnector(); | |
} | |
var outCircle = function(d) { | |
selectedNode = null; | |
updateTempConnector(); | |
} | |
var circleDragger = d3.behavior.drag() | |
.on("dragstart", function(d){ | |
draggingNode = d; | |
// it's important that we suppress the mouseover event on the node being dragged. Otherwise it will absorb the mouseover event and the underlying node will not detect it | |
d3.select(this).attr( 'pointer-events', 'none' ); | |
}) | |
.on("drag", function(d) { | |
d.x += d3.event.dx; | |
d.y += d3.event.dy; | |
var node = d3.select(this); | |
node.attr( { cx: d.x, cy: d.y } ); | |
updateTempConnector(); | |
}) | |
.on("dragend", function(d){ | |
draggingNode = null; | |
// now restore the mouseover event or we won't be able to drag a 2nd time | |
d3.select(this).attr( 'pointer-events', '' ); | |
}) | |
var updateTempConnector = function() { | |
var data = []; | |
if ( draggingNode != null && selectedNode != null) { | |
// have to flip the source coordinates since we did this for the existing connectors on the original tree | |
data = [ {source: {x: selectedNode.y, y: selectedNode.x}, | |
target: {x: draggingNode.x, y: draggingNode.y} } ]; | |
} | |
var link = vis.selectAll(".templink").data(data); | |
link.enter().append("path") | |
.attr("class", "templink") | |
.attr("d", d3.svg.diagonal() ) | |
.attr('pointer-events', 'none'); | |
link.attr("d", d3.svg.diagonal() ) | |
link.exit().remove(); | |
} | |
// ------------- normal tree drawing code -------- | |
var vis = d3.select("#viz").append("svg").attr("width", 400).attr("height", 300).append("svg:g").attr("transform", "translate(50, 0)") | |
var tree = d3.layout.tree().size([200,200]); | |
var nodes = tree.nodes(treeData); | |
var links = tree.links(nodes); | |
var diagonalHorizontal = d3.svg.diagonal().projection( function(d) { return [d.y, d.x]; } ); | |
var link = vis.selectAll(".nodelink") | |
.data(links) | |
.enter().append("path") | |
.attr("class", "nodelink") | |
.attr("d", diagonalHorizontal) | |
.attr('pointer-events', 'none'); | |
var node = vis.selectAll("g.node") | |
.data(nodes) | |
.enter().append("g") | |
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }) | |
node.append("circle") | |
.attr("r", 5) | |
.attr('pointer-events', 'none'); | |
// ------------- trickery to avoid collision detection | |
// phantom node to give us mouseover in a radius around it | |
node.append("circle") | |
.attr("r", 60) | |
.attr("opacity", 0.0) // change this to non-zero to see the target area | |
.attr('pointer-events', 'mouseover') | |
.on("mouseover", overCircle) | |
.on("mouseout", outCircle) | |
// a new, unconnected node that can be dragged near others to connect it | |
newNodes = [ {x:300,y:5, name: 'new'} ]; | |
vis.selectAll(".lonely") | |
.data(newNodes).enter().append("circle") | |
.attr("r", 5) | |
.attr("class", "lonely") | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }) | |
.call(circleDragger) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment