D3 v4添加另一个节点到数组



我得到了以下D3操场。现在的目标是创建一个固定的硬编码新节点.on("click",..),我理解节点没有得到初始的x和y位置(我猜)。让我头疼的错误信息是Unexpected value NaN parsing x1 attribute

作为一个初学者,如果有人能把我的思想引导到正确的方向,我将不胜感激。

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<html>
<head>
<title>Play with D3</title>
<!-- favcon -->
<link rel="icon" href="https://networkrepository.com/favicon.png">
<!-- call external d3.js framework -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- load "font awesome" stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>   
</head>
<style>
body {
overflow: hidden;
margin: 0px;
font-family: "Open Sans", sans-serif;
}
.canvas {
background-color: rgb(220,220,220);
}
.link {
stroke: rgb(0,0,0);
stroke-width: 2px;
}
.node {
stroke: rgb(255,255,255);
stroke-width: 2px;    
}
.icon {
fill: rgb(0,0,0);
pointer-events: none;    
}       
.node:hover{
cursor: pointer;
}
</style>
<body>
<!-- SVG area for the whole graph -->
<svg id="svg"> </svg>
<!-- call app.js where the application is written -->
<script>

// define different variables
var width = window.innerWidth
height = window.innerHeight
boolColor = true
boolOpacity = true
color = null
nodes = null

// define cavnas area to draw everything
var svg = d3.select("svg")
.attr("class", "canvas")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function() {
svg.attr("transform", d3.event.transform)
}))
.append("g")
// Removes zoom on doubleclick listener
d3.select("svg").on("dblclick.zoom", null)
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("attraceForce",d3.forceManyBody().strength(70));
// load data from json file
var data_nodes = [
{
"id": "00000", 
"type": "company", 
"name": "Test", 
"context": "",
"icon": "uf1ad"
},
{
"id": "00100",
"type": "software",
"name": "Jira",
"context":"Jira",
"icon": "uf7b1",
"parent" : "00000"
},
{
"id": "00200",
"type": "software",
"name": "Confluence",
"context":"Confluence",
"icon": "uf78d",
"parent" : "00000"
},
{
"id": "00300",
"type": "software",
"name": "IVIS",
"context":"IVIS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00400",
"type": "software",
"name": "IPOS",
"context":"IPOS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00500",
"type": "software",
"name": "IDAS",
"context":"IDAS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "99997",
"type": "hardware",
"name": "power-plug",
"context": "power-plug",
"icon": "uf1e6",
"parent" : "00000"
},
{
"id": "99998",
"type": "hardware",
"name": "usv",
"context": "usv",
"icon": "uf5df",
"parent" : "00000"
},
]
var data_links = [ 

{"source": "99998", "target": "00000"},
{"source": "99997", "target": "00000"},
{"source": "00100", "target": "00000"},
{"source": "00200", "target": "00000"},
{"source": "00300", "target": "00000"},
{"source": "00400", "target": "00000"},
{"source": "00500", "target": "00000"},

]



// create links which visualize relationships
var links = svg.selectAll("svg")
.data(data_links) 
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", 3)
.style("stroke-linecap", "round")
// disable browser context menu on link
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.select(this)
.style("stroke", "red")
})
.on("mouseleave", function(d) {
d3.select(this)
.style("stroke", "black")
}) 
var nodes = svg.selectAll("svg")
.data(data_nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("class", "node")
.attr("fill", entryColor)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", click) 

var icons = svg.selectAll("svg")
.data(data_nodes)
.enter()
//.append("g")
.append("text")
.attr("class", "icon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("font-family","FontAwesome")
.style("font-size","30px")
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
// disable browser context menu on icon
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.event.preventDefault()
})

simulation
.nodes(data_nodes)
.on("tick", ticked);

simulation
.force("link")
.links(data_links);


function ticked() {
// update link positions
links
.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; });

// update node positions
nodes
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })

// update icon positions
icons
.attr("x", function(d) {return d.x})
.attr("y", function(d) {return d.y})
}

function click(d) {
addNode()
}
function addNode() {
var obj = {
"id": "66000", 
"type": "company", 
"name": "Test1", 
"context": "",
"icon": "uf1ad"
}
data_nodes.push(obj)
}

/*
Set the color of each node in dependency of their d.name attribute.
*/
function entryColor(d) {
switch (d.name) {
case "power-plug":
return "lightgreen"
case "usv":
return "orange" 
case "diesel":
return "orange"
default:
return "whitesmoke"
}
}
/*
dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each
object. In case all drag events are not desired, simple comment out the .call(d3.drag())
execution during the object(node) creation
*/
function dragStarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

function dragEnded(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</body>
</html>

关键问题是,您需要在添加节点后重新初始化力布局:

simulation.nodes(data_nodes);

然后你可以做你的事了:

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<html>
<head>
<title>Play with D3</title>
<!-- favcon -->
<link rel="icon" href="https://networkrepository.com/favicon.png">
<!-- call external d3.js framework -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- load "font awesome" stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>   
</head>
<style>
body {
overflow: hidden;
margin: 0px;
font-family: "Open Sans", sans-serif;
}
.canvas {
background-color: rgb(220,220,220);
}
.link {
stroke: rgb(0,0,0);
stroke-width: 2px;
}
.node {
stroke: rgb(255,255,255);
stroke-width: 2px;    
}
.icon {
fill: rgb(0,0,0);
pointer-events: none;    
}       
.node:hover{
cursor: pointer;
}
</style>
<body>
<!-- SVG area for the whole graph -->
<svg id="svg"> </svg>
<!-- call app.js where the application is written -->
<script>

// define different variables
var width = window.innerWidth
height = window.innerHeight
boolColor = true
boolOpacity = true
color = null
nodes = null

// define cavnas area to draw everything
var svg = d3.select("svg")
.attr("class", "canvas")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function() {
svg.attr("transform", d3.event.transform)
}))
.append("g")
// Removes zoom on doubleclick listener
d3.select("svg").on("dblclick.zoom", null)
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("attraceForce",d3.forceManyBody().strength(70));
// load data from json file
var data_nodes = [
{
"id": "00000", 
"type": "company", 
"name": "Test", 
"context": "",
"icon": "uf1ad"
},
{
"id": "00100",
"type": "software",
"name": "Jira",
"context":"Jira",
"icon": "uf7b1",
"parent" : "00000"
},
{
"id": "00200",
"type": "software",
"name": "Confluence",
"context":"Confluence",
"icon": "uf78d",
"parent" : "00000"
},
{
"id": "00300",
"type": "software",
"name": "IVIS",
"context":"IVIS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00400",
"type": "software",
"name": "IPOS",
"context":"IPOS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00500",
"type": "software",
"name": "IDAS",
"context":"IDAS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "99997",
"type": "hardware",
"name": "power-plug",
"context": "power-plug",
"icon": "uf1e6",
"parent" : "00000"
},
{
"id": "99998",
"type": "hardware",
"name": "usv",
"context": "usv",
"icon": "uf5df",
"parent" : "00000"
},
]
var data_links = [ 

{"source": "99998", "target": "00000"},
{"source": "99997", "target": "00000"},
{"source": "00100", "target": "00000"},
{"source": "00200", "target": "00000"},
{"source": "00300", "target": "00000"},
{"source": "00400", "target": "00000"},
{"source": "00500", "target": "00000"},

]



// create links which visualize relationships
var links = svg.selectAll("svg")
.data(data_links) 
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", 3)
.style("stroke-linecap", "round")
// disable browser context menu on link
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.select(this)
.style("stroke", "red")
})
.on("mouseleave", function(d) {
d3.select(this)
.style("stroke", "black")
}) 
var nodes = svg.selectAll("svg")
.data(data_nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("class", "node")
.attr("fill", entryColor)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", click) 

var icons = svg.selectAll("svg")
.data(data_nodes)
.enter()
//.append("g")
.append("text")
.attr("class", "icon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("font-family","FontAwesome")
.style("font-size","30px")
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
// disable browser context menu on icon
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.event.preventDefault()
})

simulation
.nodes(data_nodes)
.on("tick", ticked);

simulation
.force("link")
.links(data_links);


function ticked() {
// update link positions
links
.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; });

// update node positions
nodes
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })

// update icon positions
icons
.attr("x", function(d) {return d.x})
.attr("y", function(d) {return d.y})
}

function click(d) {
addNode()
}
function addNode() {
var obj = {
"id": "66000", 
"type": "company", 
"name": "Test1", 
"context": "",
"icon": "uf1ad"
}
data_nodes.push(obj)
// changes:

// re-initialize the simulation:
simulation.nodes(data_nodes);

//append the new object:
nodes = svg.selectAll("circle") // you want to select circles, no svg here.
.data(data_nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("class", "node")
.attr("fill", entryColor)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", click)      
.merge(nodes);

icons = svg.selectAll("text")
.data(data_nodes)
.enter()
.append("text")
.attr("class", "icon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("font-family","FontAwesome")
.style("font-size","30px")
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
// disable browser context menu on icon
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.event.preventDefault()
})  
.merge(icons);

}

/*
Set the color of each node in dependency of their d.name attribute.
*/
function entryColor(d) {
switch (d.name) {
case "power-plug":
return "lightgreen"
case "usv":
return "orange" 
case "diesel":
return "orange"
default:
return "whitesmoke"
}
}
/*
dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each
object. In case all drag events are not desired, simple comment out the .call(d3.drag())
execution during the object(node) creation
*/
function dragStarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

function dragEnded(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</body>
</html>

链接也一样,你需要初始化它们:

simulation.force("link").links(data_links);

<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<html>
<head>
<title>Play with D3</title>
<!-- favcon -->
<link rel="icon" href="https://networkrepository.com/favicon.png">
<!-- call external d3.js framework -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- load "font awesome" stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>   
</head>
<style>
body {
overflow: hidden;
margin: 0px;
font-family: "Open Sans", sans-serif;
}
.canvas {
background-color: rgb(220,220,220);
}
.link {
stroke: rgb(0,0,0);
stroke-width: 2px;
}
.node {
stroke: rgb(255,255,255);
stroke-width: 2px;    
}
.icon {
fill: rgb(0,0,0);
pointer-events: none;    
}       
.node:hover{
cursor: pointer;
}
</style>
<body>
<!-- SVG area for the whole graph -->
<svg id="svg"> </svg>
<!-- call app.js where the application is written -->
<script>

// define different variables
var width = window.innerWidth
height = window.innerHeight
boolColor = true
boolOpacity = true
color = null
nodes = null

// define cavnas area to draw everything
var svg = d3.select("svg")
.attr("class", "canvas")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function() {
svg.attr("transform", d3.event.transform)
}))
.append("g")
// Removes zoom on doubleclick listener
d3.select("svg").on("dblclick.zoom", null)
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("attraceForce",d3.forceManyBody().strength(70));
// load data from json file
var data_nodes = [
{
"id": "00000", 
"type": "company", 
"name": "Test", 
"context": "",
"icon": "uf1ad"
},
{
"id": "00100",
"type": "software",
"name": "Jira",
"context":"Jira",
"icon": "uf7b1",
"parent" : "00000"
},
{
"id": "00200",
"type": "software",
"name": "Confluence",
"context":"Confluence",
"icon": "uf78d",
"parent" : "00000"
},
{
"id": "00300",
"type": "software",
"name": "IVIS",
"context":"IVIS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00400",
"type": "software",
"name": "IPOS",
"context":"IPOS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "00500",
"type": "software",
"name": "IDAS",
"context":"IDAS",
"icon": "ue084",
"parent" : "00000"
},
{
"id": "99997",
"type": "hardware",
"name": "power-plug",
"context": "power-plug",
"icon": "uf1e6",
"parent" : "00000"
},
{
"id": "99998",
"type": "hardware",
"name": "usv",
"context": "usv",
"icon": "uf5df",
"parent" : "00000"
},
]
var data_links = [ 

{"source": "99998", "target": "00000"},
{"source": "99997", "target": "00000"},
{"source": "00100", "target": "00000"},
{"source": "00200", "target": "00000"},
{"source": "00300", "target": "00000"},
{"source": "00400", "target": "00000"},
{"source": "00500", "target": "00000"},

]



// create links which visualize relationships
var links = svg.selectAll("svg")
.data(data_links) 
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", 3)
.style("stroke-linecap", "round")
// disable browser context menu on link
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.select(this)
.style("stroke", "red")
})
.on("mouseleave", function(d) {
d3.select(this)
.style("stroke", "black")
}) 
var nodes = svg.selectAll("svg")
.data(data_nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("class", "node")
.attr("fill", entryColor)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", click) 

var icons = svg.selectAll("svg")
.data(data_nodes)
.enter()
//.append("g")
.append("text")
.attr("class", "icon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("font-family","FontAwesome")
.style("font-size","30px")
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
// disable browser context menu on icon
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.event.preventDefault()
})

simulation
.nodes(data_nodes)
.on("tick", ticked);

simulation
.force("link")
.links(data_links);


function ticked() {
// update link positions
links
.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; });

// update node positions
nodes
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })

// update icon positions
icons
.attr("x", function(d) {return d.x})
.attr("y", function(d) {return d.y})
}

function click(d) {
addNode(d)
}
function addNode(d) {
var id = Math.round(Math.random()*10000);
var obj = {
"id": id, 
"type": "company", 
"name": "Test1", 
"context": "",
"icon": "uf1ad"
}
data_nodes.push(obj)
// changes:

// re-initialize the simulation:
simulation.nodes(data_nodes);

//append the new object:
nodes = svg.selectAll("circle")
.data(data_nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("class", "node")
.attr("fill", entryColor)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", click)      
.merge(nodes);

icons = svg.selectAll("text")
.data(data_nodes)
.enter()
.append("text")
.attr("class", "icon")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("font-family","FontAwesome")
.style("font-size","30px")
.text(function (d) {return d.icon;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
// disable browser context menu on icon
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.event.preventDefault()
})  
.merge(icons);

// for links:
var newLink = {source: id, target: d.id } 
data_links.push(newLink);

simulation.force("link").links(data_links);
links = svg.selectAll("line")
.data(data_links) 
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", 3)
.style("stroke-linecap", "round")
// disable browser context menu on link
.on("contextmenu", function (d, i){
d3.event.preventDefault()
})
.on("mouseenter", function(d) {
d3.select(this)
.style("stroke", "red")
})
.on("mouseleave", function(d) {
d3.select(this)
.style("stroke", "black")
})       
.merge(links);

}

/*
Set the color of each node in dependency of their d.name attribute.
*/
function entryColor(d) {
switch (d.name) {
case "power-plug":
return "lightgreen"
case "usv":
return "orange" 
case "diesel":
return "orange"
default:
return "whitesmoke"
}
}
/*
dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each
object. In case all drag events are not desired, simple comment out the .call(d3.drag())
execution during the object(node) creation
*/
function dragStarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

function dragEnded(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</body>
</html>

最新更新