我是D3的新手,我正在尝试修改此示例以制作相对静止的有向图:http://bl.ocks.org/rkirsling/5001347
现在我拥有它,以便为每个节点分配一个位置并将它们设置为固定。这通常有效但节点卡住且无生命。我想要的是将节点分配的位置作为它们的位置,但是如果我拖动其中一个节点,它会移回到它的位置,就像这个例子中的引力一样:http://bl.ocks.org/mbostock/1804919
如何修改以下代码以包含此内容?
function start(element, numNodes, numEdges) {
// set up SVG for D3
var width = 960,
height = 500,
colors = d3.scale.category10();
var svg = d3.select("#visrep").append('svg').attr('oncontextmenu', 'return false;').attr('width', width).attr('height', height);
//var force = d3.layout.force().gravity(0.05).distance(100).charge(-100).size([width, height]);
//var force = cola.d3adaptor().avoidOverlaps(true).linkDistance(30).size([width, height]).nodes(nodes).links(links).size([width, height]);
// set up initial nodes and links
// - nodes are known by 'id', not by index in array.
// - reflexive edges are indicated on the node (as a bold black circle).
// - links are always source < target; edge directions are set by 'left' and 'right'.
var nodes, links;
var nodes = [
{ id: 0, reflexive: false },
{ id: 1, reflexive: true },
{ id: 2, reflexive: false }
],
lastNodeId = 2;
nodes = [];
var count = 0, lineLimit = 10;
for (var i = 0; i < numNodes; i++) {
if (nod[i].nodeID < lineLimit) count++;
}
var L = 50, r = 12;
var d = 2 * r + L;
var R = (count - 1) * d;
var m = width / 2;
var X;
//alert("L: " + L + " r: " + r + " d: " + d + " R: " + R + " m: " + m);
for (var i = 0; i < numNodes; i++) {
if (nod[i].nodeID <= lineLimit) {
X = m - (R / 2) + (i * d);
// alert("doing node: " + nod[i].nodeID + " at => " + X);
nodes.push({ x: X, y: (height)/3, id: nod[i].nodeID, reflexive: true });
}
else {
X = m + (R / 2) - ((i-count) * d);
// alert("doing node: " + nod[i].nodeID + " at => " + X);
nodes.push({ x: X, y: (height * 2) / 3, id: nod[i].nodeID, reflexive: true });
}
}
lastNodeId = numNodes - 1;
var links = [
{ source: nodes[0], target: nodes[1], left: false, right: true },
{ source: nodes[1], target: nodes[2], left: false, right: true }
]
links = [];
for (var i = 0; i < numEdges; ++i) {
//d3.select(element).append("h3").text("Source: " + nodes[edges[i].source - 1].id + " Target: " + nodes[edges[i].target - 1].id);
links.push({ source: nodes[edges[i].source - 1], target: nodes[edges[i].target - 1], left: false, right: true });
}
// init D3 force layout
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([width, height])
.linkDistance(150)
.charge(-500)
.on('tick', tick);
//var force = cola.d3adaptor().avoidOverlaps(true).size([width, height]).nodes(nodes).links(links).size([width, height]).on('tick', tick);
// define arrow markers for graph links
svg.append('svg:defs').append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 6)
.attr('markerWidth', 3)
.attr('markerHeight', 3)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#000');
svg.append('svg:defs').append('svg:marker')
.attr('id', 'start-arrow')
.attr('viewBox', '0 -5 10 10')
.attr('refX', 4)
.attr('markerWidth', 3)
.attr('markerHeight', 3)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M10,-5L0,0L10,5')
.attr('fill', '#000');
// line displayed when dragging new nodes
var drag_line = svg.append('svg:path')
.attr('class', 'link dragline hidden')
.attr('d', 'M0,0L0,0');
// handles to link and node element groups
var path = svg.append('svg:g').selectAll('path'),
circle = svg.append('svg:g').selectAll('g');
// mouse event vars
var selected_node = null,
selected_link = null,
mousedown_link = null,
mousedown_node = null,
mouseup_node = null;
function resetMouseVars() {
mousedown_node = null;
mouseup_node = null;
mousedown_link = null;
}
var n = nodes.length; nodes.forEach(function (d, i) {
if (d.Category == "Chip Life Cycle") {
//d.x = (width / (n * i)) + 10;
//d.y = height * 0.75;
d.fixed = true;
}
else if (d.Category == "Abstraction") {
//d.x = (width / (n * i)) + 10;
//d.y = height * 0.75;
d.fixed = true;
}
else if (d.Category == "Properties") {
//d.x = (width / (n * i)) + 10;
//d.y = height * 0.4;
d.fixed = true;
}
else {
//d.x = (width / (n * i)) + 10;
//d.y = height * 0.4;
d.fixed = true;
}
++i;
});
// update force layout (called automatically each iteration)
function tick() {
// draw directed edges with proper padding from node centers
path.attr('d', function (d) {
var deltaX = d.target.x - d.source.x,
deltaY = d.target.y - d.source.y,
dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
normX = deltaX / dist,
normY = deltaY / dist,
sourcePadding = d.left ? 17 : 12,
targetPadding = d.right ? 17 : 12,
sourceX = d.source.x + (sourcePadding * normX),
sourceY = d.source.y + (sourcePadding * normY),
targetX = d.target.x - (targetPadding * normX),
targetY = d.target.y - (targetPadding * normY);
return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
});
circle.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
}
// update graph (called when needed)
function restart() {
// path (link) group
path = path.data(links);
// update existing links
path.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });
// add new links
path.enter().append('svg:path')
.attr('class', 'link')
.classed('selected', function (d) { return d === selected_link; })
.style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
.style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select link
mousedown_link = d;
if (mousedown_link === selected_link) selected_link = null;
else selected_link = mousedown_link;
selected_node = null;
restart();
});
// remove old links
path.exit().remove();
// circle (node) group
// NB: the function arg is crucial here! nodes are known by id, not by index!
circle = circle.data(nodes, function (d) { return d.id; });
// update existing nodes (reflexive & selected visual states)
circle.selectAll('circle')
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.classed('reflexive', function (d) { return d.reflexive; });
// add new nodes
var g = circle.enter().append('svg:g');
g.append('svg:circle')
.attr('class', 'node')
.attr('r', 12)
.style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
.style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
.classed('reflexive', function (d) { return d.reflexive; })
.on('mouseover', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// enlarge target node
d3.select(this).attr('transform', 'scale(1.1)');
})
.on('mouseout', function (d) {
if (!mousedown_node || d === mousedown_node) return;
// unenlarge target node
d3.select(this).attr('transform', '');
})
.on('mousedown', function (d) {
if (d3.event.ctrlKey) return;
// select node
mousedown_node = d;
if (mousedown_node === selected_node) selected_node = null;
else selected_node = mousedown_node;
selected_link = null;
// reposition drag line
drag_line
.style('marker-end', 'url(#end-arrow)')
.classed('hidden', false)
.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);
restart();
})
.on('mouseup', function (d) {
if (!mousedown_node) return;
// needed by FF
drag_line
.classed('hidden', true)
.style('marker-end', '');
// check for drag-to-self
mouseup_node = d;
if (mouseup_node === mousedown_node) { resetMouseVars(); return; }
// unenlarge target node
d3.select(this).attr('transform', '');
// add link to graph (update if exists)
// NB: links are strictly source < target; arrows separately specified by booleans
var source, target, direction;
if (mousedown_node.id < mouseup_node.id) {
source = mousedown_node;
target = mouseup_node;
direction = 'right';
} else {
source = mouseup_node;
target = mousedown_node;
direction = 'left';
}
var link;
link = links.filter(function (l) {
return (l.source === source && l.target === target);
})[0];
if (link) {
link[direction] = true;
} else {
link = { source: source, target: target, left: false, right: false };
link[direction] = true;
links.push(link);
}
// select new link
selected_link = link;
selected_node = null;
restart();
});
// show node IDs
g.append('svg:text')
.attr('x', 0)
.attr('y', 4)
.attr('class', 'id')
.text(function (d) { return d.id; });
// remove old nodes
circle.exit().remove();
// set the graph in motion
force.start();
}
function mousedown() {
// prevent I-bar on drag
//d3.event.preventDefault();
// because :active only works in WebKit?
svg.classed('active', true);
if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;
// insert new node at point
var point = d3.mouse(this),
node = { id: ++lastNodeId, reflexive: false };
node.x = point[0];
node.y = point[1];
nodes.push(node);
restart();
}
function mousemove() {
if (!mousedown_node) return;
// update drag line
drag_line.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);
restart();
}
function mouseup() {
if (mousedown_node) {
// hide drag line
drag_line
.classed('hidden', true)
.style('marker-end', '');
}
// because :active only works in WebKit?
svg.classed('active', false);
// clear mouse event vars
resetMouseVars();
}
function spliceLinksForNode(node) {
var toSplice = links.filter(function (l) {
return (l.source === node || l.target === node);
});
toSplice.map(function (l) {
links.splice(links.indexOf(l), 1);
});
}
// only respond once per keydown
var lastKeyDown = -1;
function keydown() {
d3.event.preventDefault();
if (lastKeyDown !== -1) return;
lastKeyDown = d3.event.keyCode;
// ctrl
if (d3.event.keyCode === 17) {
circle.call(force.drag);
svg.classed('ctrl', true);
}
if (!selected_node && !selected_link) return;
switch (d3.event.keyCode) {
case 8: // backspace
case 46: // delete
if (selected_node) {
nodes.splice(nodes.indexOf(selected_node), 1);
spliceLinksForNode(selected_node);
} else if (selected_link) {
links.splice(links.indexOf(selected_link), 1);
}
selected_link = null;
selected_node = null;
restart();
break;
case 66: // B
if (selected_link) {
// set link direction to both left and right
selected_link.left = true;
selected_link.right = true;
}
restart();
break;
case 76: // L
if (selected_link) {
// set link direction to left only
selected_link.left = true;
selected_link.right = false;
}
restart();
break;
case 82: // R
if (selected_node) {
// toggle node reflexivity
selected_node.reflexive = !selected_node.reflexive;
} else if (selected_link) {
// set link direction to right only
selected_link.left = false;
selected_link.right = true;
}
restart();
break;
}
}
function keyup() {
lastKeyDown = -1;
// ctrl
if (d3.event.keyCode === 17) {
circle
.on('mousedown.drag', null)
.on('touchstart.drag', null);
svg.classed('ctrl', false);
}
}
// app starts here
svg.on('mousedown', mousedown)
.on('mousemove', mousemove)
.on('mouseup', mouseup);
d3.select(window)
.on('keydown', keydown)
.on('keyup', keyup);
restart();
}
var nod;
var edges;
function visualize(element, numEdges, numNodes) {
//start();
//graph = new Visualizer(element);
for (var i = 0; i < numEdges ; ++i) {
//graph.addLink(edges[i].source, nodeEdge[i].target);
d3.select(element).append("h3").text("Source: "+ edges[i].source + " Target: " + edges[i].target);
}
for (var i = 0; i < numNodes; ++i) {
//graph.addNode(node[i].nodeID, nodes[i].Category);
d3.select(element).append("h3").text("Node: " + nod[i].nodeID + " Category: " + nod[i].Category);
}
start(element, numNodes, numEdges);
// //graph.start();
}
修改
根据要求,HTML虽然相当无聊。我的C#将div元素&#34;#visrep&#34;传递给visualize函数。在&#39;元素&#39;参数。
<asp:Content ID="DescriptionContent" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript" src="Scripts/d3.js"></script>
<div id="visJumboContainer" class="jumbotron">
<div id="visrep" class="ContentHead"></div>
</div>
<script type="text/javascript">
<%--This is where the script from above sits--%>
</script>
</asp:Content>