[英]D3 adding nodes in force-directed graph, how to in v5 (vs. v3)
我發現了一個很好的 FD Graph 腳本,可以交互地添加和刪除節點,用 D3 v3 編寫。 (見http://bl.ocks.org/tgk/6068367 )。 我試圖將它移植到 v5,但它不起作用。 我構建了以下腳本來比較兩個版本。 我發現的任何示例,只需切換鏈接數據(參見https://bl.ocks.org/colbenkharrl/21b3808492b93a21de841bc5ceac4e47 ),而不是節點數據。
任何人都可以幫忙嗎?
切換D3版本請看代碼第34行!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
html {
background-color: black;
}
svg {
background-color: white;
}
rect {
fill: none;
pointer-events: all;
}
.node {
fill: #000;
}
.cursor {
fill: none;
stroke: brown;
pointer-events: none;
}
.link {
stroke: #999;
}
</style>
<body>
<script>
let version = 5;
let loader = 'document.write("<' + 'script src=\\"https://d3js.org/d3.v' + version + '.min.js\\"></' +
'script>' +
'");';
eval(loader);
</script>
<script>
!(function () {
'use strict';
// - - - - - - - - - -
// 1. Global declaration
// - - - - - - - - - -
let
fill, simulation, svg, nodes, links, node, link, cursor,
i = false,
canvas = {},
dataset = {};
// Starting with an empty dataset
dataset.nodes = [{}];
dataset.links = [{}];
canvas = {
width: 800,
height: 400
};
// - - - - - - - - - -
// 2. Functions
// - - - - - - - - - -
// 2.1. Eventhandler functions
// - - - - - - - - - -
function onCanvasMousemove() {
cursor
.attr("transform", "translate(" + d3.mouse(this) + ")");
}
function onCanvasMousedown() {
let point = d3.mouse(this),
node = {
x: point[0],
y: point[1]
},
n = nodes.push(node);
// add links to any nearby nodes
nodes
.forEach(function (target) {
let x = target.x - node.x,
y = target.y - node.y;
if (Math.sqrt(x * x + y * y) < 30) {
links.push({
source: node,
target: target
});
}
});
restartSimulation();
}
function onNodeMousedown(d, i) {
nodes.splice(i, 1);
links = links.filter(function (l) {
return l.source !==
d && l.target !== d;
});
d3.event.stopPropagation();
restartSimulation();
}
function onTicked() {
link
.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;
});
node
.attr("cx", function (d) {
if (i === false) {
console.log(typeof node);
console.log(typeof d);
i = true;
}
return d.x;
}).attr("cy", function (d) {
return d.y;
});
}
// - - - - - - - - - -
// 2.2 Setter functions
// - - - - - - - - - -
function setSvg() {
svg = d3.select("body")
.append("svg")
.attr("width", canvas.width)
.attr("height", canvas.height)
.on("mousemove", onCanvasMousemove)
.on("mousedown", onCanvasMousedown);
svg.append("rect")
.attr("width", canvas.width)
.attr("height", canvas.height);
}
function setCursor() {
// create the cursor
cursor = svg.append("circle")
.attr("r", 30)
.attr("transform", "translate(-100,-100)")
.attr("class", "cursor");
}
function setColorScheme() {
switch (version) {
case 3:
fill = d3.scale.category20();
break;
case 5:
d3.scaleOrdinal(d3.schemeCategory10);
break;
}
}
/** Create the force direction graph
*/
function setSimulation() {
switch (version) {
case 3:
simulation = d3.layout
.force()
.size([canvas.width, canvas.height])
.nodes(dataset.nodes) // ! data, initialize with a single node
.linkDistance(30)
.charge(-60)
.on("tick", onTicked);
break;
case 4:
case 5:
simulation = d3.forceSimulation()
.nodes(dataset.nodes)
.force('link', d3.forceLink().distance(30))
.force('charge',
d3.forceManyBody()
.strength(-60))
.force('center', d3.forceCenter(canvas.width / 2, canvas.height / 2))
.on('tick', onTicked);
break;
}
restartSimulation();
}
function restartSimulation() {
// - - - - - - - - - -
// NODES
// - - - - - - - - - -
switch (version) {
case 3:
node = svg.selectAll('.node');
nodes = simulation.nodes();
node = node.data(nodes);
node.exit().remove();
node.enter()
.insert("circle", ".cursor")
.attr("class", "node")
.attr("r", 5)
.on("mousedown", onNodeMousedown);
break;
case 4:
case 5:
node = svg.selectAll('.node');
nodes = simulation.nodes();
node = node.data(nodes);
node.exit().remove();
node.enter()
.insert("circle", ".cursor")
.attr("class", "node")
.attr("r", 5)
.merge(node)
.on("mousedown", onNodeMousedown);
break;
}
// - - - - - - - - - -
// LINKS
// - - - - - - - - - -
switch (version) {
case 3:
link = svg.selectAll('.link');
links = simulation.links();
link = link.data(links);
link.exit().remove();
link.enter()
.insert("line", ".node")
.attr("class", "link")
break;
case 4:
case 5:
links = svg.selectAll('.link')
link = link.data(links);
link.exit().remove();
link.enter()
.append("line", ".node")
.attr("class", "link")
.merge(link);
break;
}
switch (version) {
case
3:
simulation.start();
break;
case 5:
simulation.alphaTarget(0.3).restart();
break;
}
}
// - - - - - - - - - -
// 2.3 Control functions
// - - - - - - - - - -
function main() {
setSvg();
setSimulation();
setCursor();
setColorScheme();
}
function init() {
main();
};
// - - - - - - - - - -
// 3. Main control
// - - - - - - - - - -
window.addEventListener('load', init);
// - - - - - - - - - -
}())
</script>
好,我知道了。 關鍵的注釋是https://bl.ocks.org/tezzutezzu/cd04b3f1efee4186ff42aae66c87d1a7 。
有很多帶有變量的微小重構,隨意區分前后。 在我的第一個版本中刪除 v5->merge() 並清除它適用於 D3v3 和 v5 的變量后。 並且沒有必要使用不同的數據集,正如我首先想到的那樣,一個就足夠了。 另一件事是寫
nodeElements = svg.selectAll(".node").data(dataset.nodes, function (d) {
return d.id
});
代替
nodeElements = svg.selectAll(".node");
nodeElements.data(dataset.nodes, function (d) {
return d.id
});
到目前為止,這是代碼。 我還更新了 codepen 示例。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
html {
background-color: black;
}
svg {
background-color: white;
}
rect {
fill: none;
pointer-events: all;
}
.node {
fill: #000;
}
.cursor {
fill: none;
stroke: brown;
pointer-events: none;
}
.link {
stroke: #999;
}
</style>
<body>
<script>
let version = 5;
let loader = 'document.write("<' + 'script src=\\"https://d3js.org/d3.v' + version + '.min.js\\"></' +
'script>' +
'");';
eval(loader);
</script>
<script>
!(function () {
'use strict';
// - - - - - - - - - -
// 1. Global declaration
// - - - - - - - - - -
let
fill, simulation, svg, nodes, links, node, link, cursor,
nodeElements, linkElements,
i = false,
canvas = {},
dataset = {};
dataset.nodes = [];
dataset.links = [];
canvas = {
width: 800,
height: 400
};
// - - - - - - - - - -
// 2. Functions
// - - - - - - - - - -
// 2.1. Eventhandler functions
// - - - - - - - - - -
function onCanvasMousemove() {
cursor
.attr("transform", "translate(" + d3.mouse(this) + ")");
}
function onCanvasMousedown() {
let
point = d3.mouse(this),
node = {
id: String.fromCharCode(Math.floor(Math.random() * 24) + 65),
x: point[0],
y: point[1]
};
dataset.nodes.push(node);
// add links to any nearby nodes
dataset.nodes
.forEach(function (target) {
let x = target.x - node.x,
y = target.y - node.y;
if (Math.sqrt(x * x + y * y) < 30) {
dataset.links.push({
source: node,
target: target
});
}
});
startSimulation();
}
function onNodeMousedown(d, i) {
dataset.nodes.splice(i, 1);
dataset.links = dataset.links.filter(function (l) {
return l.source !==
d && l.target !== d;
});
d3.event.stopPropagation();
startSimulation();
}
function onTicked() {
let nodeElements = svg.selectAll('.node');
let linkElements = svg.selectAll('.link');
linkElements
.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;
});
nodeElements
.attr("cx", function (d) {
if (i === false) {
console.log(typeof node);
console.log(typeof d);
i = true;
}
return d.x;
}).attr("cy", function (d) {
return d.y;
});
}
// - - - - - - - - - -
// 2.2 Setter functions
// - - - - - - - - - -
function setSvg() {
svg = d3.select("body")
.append("svg")
.attr("width", canvas.width)
.attr("height", canvas.height)
.on("mousemove", onCanvasMousemove)
.on("mousedown", onCanvasMousedown);
svg.append("rect")
.attr("width", canvas.width)
.attr("height", canvas.height);
}
function setCursor() {
// create the cursor
cursor = svg.append("circle")
.attr("r", 30)
.attr("transform", "translate(-100,-100)")
.attr("class", "cursor");
}
function setColorScheme() {
switch (version) {
case 3:
fill = d3.scale.category20();
break;
case 5:
d3.scaleOrdinal(d3.schemeCategory10);
break;
}
}
/** Create the force direction graph
*/
function setSimulation() {
switch (version) {
case 3:
simulation = d3.layout
.force()
.size([canvas.width, canvas.height])
.nodes(dataset.nodes) // ! data, initialize with a single node
.linkDistance(30)
.charge(-60)
.on("tick", onTicked);
break;
case 4:
case 5:
simulation = d3.forceSimulation()
// .nodes(dataset.nodes)
.force('link', d3.forceLink()
.distance(200)
.strength(0.6)
)
.force('charge',
d3.forceManyBody()
.strength(-60)
)
.force("x", d3.forceX(canvas.width / 2))
.force("y", d3.forceY(canvas.height / 2))
// .force('center', d3.forceCenter(canvas.width / 2, canvas.height / 2))
.on('tick', onTicked);
break;
}
startSimulation();
}
function startSimulation() {
// - - - - - - - - - -
// NODES
// - - - - - - - - - -
switch (version) {
case 3:
nodeElements = svg.selectAll('.node');
dataset.nodes = simulation.nodes();
node = nodeElements.data(dataset.nodes);
node.exit().remove();
node.enter()
.insert("circle", ".cursor")
.attr("class", "node")
.attr("r", 5)
.on("mousedown", onNodeMousedown);
break;
case 4:
case 5:
nodeElements = svg.selectAll(".node")
.data(dataset.nodes, function (d) {
return d.id
});
nodeElements.enter()
.append("circle")
.attr("class", function (d) {
return "node " + d.id;
})
.attr("r", 8)
.on("mousedown", onNodeMousedown);
nodeElements.exit().remove();
simulation.nodes(dataset.nodes)
simulation.force("link").links(dataset.links)
simulation.restart();
break;
}
// - - - - - - - - - -
// LINKS
// - - - - - - - - - -
switch (version) {
case 3:
link = svg.selectAll('.link');
links = simulation.links();
link = link.data(links);
link.exit().remove();
link.enter()
.insert("line", ".node")
.attr("class", "link")
break;
case 4:
case 5:
linkElements = svg
.selectAll('.link')
.data(dataset.links);
linkElements.exit().remove();
linkElements.enter()
.append("line", ".node")
.attr("class", "link");
break;
}
switch (version) {
case
3:
simulation.start();
break;
case 5:
simulation.alphaTarget(0.3).restart();
break;
}
}
// - - - - - - - - - -
// 2.3 Control functions
// - - - - - - - - - -
function main() {
setSvg();
setSimulation();
setCursor();
setColorScheme();
}
function init() {
main();
};
// - - - - - - - - - -
// 3. Main control
// - - - - - - - - - -
window.addEventListener('load', init);
// - - - - - - - - - -
}())
</script>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.