score:1
The issue is that the nodes
and the links
haven't been added to the respective arrays when you call the refresh()
method.
Here's the explanation to when the onload
function gets called (mentioned in the MSDN web docs):
The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.
So by the time refresh is called after setting all the variables (in the <script>
tag, the connect1
hasn't been called yet which means there are no nodes and no links and hence no graph. But when you click the the refresh button, the connect1
has already been executed and the nodes, links have been added which leads to drawing the graph.
Amongst many solutions to this, here's one approach: (calling refresh
in the connect1
function)
<!DOCTYPE html>
<html>
<body onload="connect1();">
<canvas width="300" height="100"></canvas>
<!--button id="ref" onclick="refresh()">refresh </button-->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
var links =[] , nodes = [] ;
var graph={nodes,links}, wsConn;
function connect1(){
addNodeCanvas("A");
addNodeCanvas("B");
addNodeCanvas("C");
addNodeCanvas("D");
addNodeCanvas("E");
addLinkCanvas("A","B");
addLinkCanvas("A","C");
addLinkCanvas("D","C");
addLinkCanvas("E","D");
addLinkCanvas("E","B");
refresh();
}
function addNodeCanvas(nodeName,g) {
var node = {
x: 100,
y: 100,
id: nodeName,
grp:g
};
var n = nodes.push(node);
}
function addLinkCanvas(idSrc, idTarget) {
if (idSrc != idTarget) {
var s = {},
t = {};
nodes.forEach(function(curNode) {
if (typeof curNode.id != "undefined") {
if (curNode.id == idSrc) {
s = curNode;
}
if (curNode.id == idTarget) {
t = curNode;
}
}
});
links.push({
source: s,
target: t
});
};
}
function refresh() {
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
simulation
.nodes(nodes)
.on("tick", ticked)
.force("link")
.links(links);
d3.select(canvas)
.call(d3.drag()
.container(canvas)
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
var margin = 20;
nodes.forEach(function(d) {
d.x = Math.max(margin, Math.min(width - margin, d.x))
d.y = Math.max(margin, Math.min(height - margin, d.y))
});
function dblclick() {
nodes.forEach(function(d) {
d.fx = d.fy = null;
})
};
context.clearRect(0, 0, width, height);
context.beginPath();
links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
nodes.forEach(drawNode);
}
function dragsubject() {
return simulation.find(d3.event.x, d3.event.y);
}
var clickDate = new Date();
var difference_ms;
function dragstarted() {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d3.event.subject.fx = Math.max(10, Math.min(width - 10, d3.event.subject.x));
d3.event.subject.fy = Math.max(10, Math.min(height - 10, d3.event.subject.y));
}
function dragged() {
d3.event.subject.fx = Math.max(10, Math.min(width - 10, d3.event.x));
d3.event.subject.fy = Math.max(10, Math.min(height - 10, d3.event.y));
}
function dragended() {
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
var nodeColors = d3.scaleOrdinal().range(d3.schemeCategory20),
labelColors = d3.scaleOrdinal().range(['red', 'orange', 'blue', 'green', 'purple']);
function drawNode(d) {
context.beginPath();
context.moveTo(d.x + 10, d.y);
context.arc(d.x, d.y, 10, 0, 2 * Math.PI);
context.strokeStyle = "#fff";
context.stroke();
context.fillStyle = nodeColors(d.grp);
context.closePath();
context.fill();
context.beginPath();
context.font = (d.labelSize ? d.labelSize : 10) + 'px Arial';
context.fillStyle = labelColors(d.grp);
context.fillText(d.id ? d.id : d.grp, d.x, d.y);
context.closePath();
}
}
</script>
</body>
</html>
EDIT: Call refresh after every node/link addition:
This bl.ocks snippet helped in knowing what has to be done to add a dynamic force layout which clearly indicates that the setup has to be done just once but the nodes/links attachment to the simulation can be done any number of times. Also, I'm using .restart()
that:
Restarts the simulation’s internal timer and returns the simulation. In conjunction with simulation.alphaTarget or simulation.alpha, this method can be used to “reheat” the simulation during interaction, such as when dragging a node, or to resume the simulation after temporarily pausing it with simulation.stop.
Here's the doc link: d3 simulation restart
The example I refer to is a SVG one but the logic is pretty much the same for a canvas.
<!DOCTYPE html>
<html>
<body >
<canvas width="900" height="600"></canvas>
<button id="ref" onclick="refresh()">refresh </button>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
var links =[] , nodes = [] ;
var graph={nodes,links}, wsConn;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
refresh();
connect1();
function connect1(){
addNodeCanvas("A");
addNodeCanvas("B");
addNodeCanvas("C");
addNodeCanvas("D");
addNodeCanvas("E");
addLinkCanvas("A", "B");
addLinkCanvas("A", "C");
addLinkCanvas("D", "C");
addLinkCanvas("E", "D");
addLinkCanvas("E", "B");
refresh();
}
function addNodeCanvas(nodeName,g) {
var node = {
x: 100,
y: 100,
id: nodeName,
grp:g
};
var n = nodes.push(node);
//console.log(node);
refresh();
}
function addLinkCanvas(idSrc, idTarget) {
if (idSrc != idTarget) {
var s = {},
t = {};
nodes.forEach(function(curNode) {
if (typeof curNode.id != "undefined") {
if (curNode.id == idSrc) {
s = curNode;
}
if (curNode.id == idTarget) {
t = curNode;
}
}
});
//console.log( { s,t});
links.push({
source: s,
target: t
});
};
refresh();
}
function refresh() {
simulation
.nodes(nodes)
.on("tick", ticked);
simulation
.force("link")
.links(links);
simulation.alpha(1).restart();
d3.select(canvas)
.call(d3.drag()
.container(canvas)
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
var margin = 20;
nodes.forEach(function(d) {
d.x = Math.max(margin, Math.min(width - margin, d.x))
d.y = Math.max(margin, Math.min(height - margin, d.y))
});
function dblclick() {
nodes.forEach(function(d) {
d.fx = d.fy = null;
})
};
context.clearRect(0, 0, width, height);
context.beginPath();
links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
nodes.forEach(drawNode);
}
function dragsubject() {
return simulation.find(d3.event.x, d3.event.y);
}
var clickDate = new Date();
var difference_ms;
function dragstarted() {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d3.event.subject.fx = Math.max(10, Math.min(width - 10, d3.event.subject.x));
d3.event.subject.fy = Math.max(10, Math.min(height - 10, d3.event.subject.y));
}
function dragged() {
d3.event.subject.fx = Math.max(10, Math.min(width - 10, d3.event.x));
d3.event.subject.fy = Math.max(10, Math.min(height - 10, d3.event.y));
}
function dragended() {
if (!d3.event.active) simulation.alphaTarget(0);
// Time between 2 ends of drag:
difference_ms = (new Date()).getTime() - clickDate.getTime();
clickDate = new Date();
// if the time between these 2 ends of drag is short enough, then
// it's considered a double click:
if (difference_ms < 200) {
// And we can release the node:
simulation.alphaTarget(0.3).restart()
d3.event.subject.fx = null;
d3.event.subject.fy = null;
}
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
var nodeColors = d3.scaleOrdinal().range(d3.schemeCategory20),
labelColors = d3.scaleOrdinal().range(['red', 'orange', 'blue', 'green', 'purple']);
function drawNode(d) {
context.beginPath();
context.moveTo(d.x + 10, d.y);
context.arc(d.x, d.y, 10, 0, 2 * Math.PI);
context.strokeStyle = "#fff";
context.stroke();
context.fillStyle = nodeColors(d.grp);
context.closePath();
context.fill();
context.beginPath();
context.font = (d.labelSize ? d.labelSize : 10) + 'px Arial';
context.fillStyle = labelColors(d.grp);
context.fillText(d.id ? d.id : d.grp, d.x, d.y);
context.closePath();
}
}
</script>
</body>
</html>
And here's the jsfiddle. Hope this helps.
Source: stackoverflow.com
Related Query
- D3 Canvas Network does not appear unless it is manually refreshed
- Tooltip does not appear for the overlapped circles in d3.js
- Chart does not reflect active entries unless I set it to array.slice() in the markup -- this will disable selecting points - Stackblitz inside
- d3 tree tooltip does not appear
- The nested in foreignobject svg does not appear
- ("svg:rect") element does not appear on the screen when run
- Why does the text not appear inside the svg rects
- D3.js graph does not appear when source is saved in file
- Title does not appear on my D3js radar chart
- SVG foreignObject contents do not display unless plain text
- NVD3 chart fails to calculate legend text length in Chrome, since Window.getComputedStyle does not return font-size correctly
- Importing local json file using d3.json does not work
- D3js v4: scaleOrdinal does not have the rangePoints()
- transitionDuration function does not exist in nvd3.js
- Angular 2 typescript d3 type issue: Property 'x' does not exist on type '[number, number]'
- Svg clip-path within rectangle does not work
- IE11 does not accept SVG height and width when using D3
- d3.transition().attr('x', y) does not work when d3.attr('x', y) does
- d3.on("mouseover") event does not work with nested SVG elements
- 'scale' and 'svg' does not exist in "node_modules/@types/d3/index"
- Property 'forEach' does not exist on type '{}'
- d3 (v4) tree layout and typescript: Property 'x' does not exist on type 'HierarchyNode<IOrgChartNode>'
- d3.tree => transform does not seem to work
- C3.js SVG visualization to Canvas with canvg - Line charts filled with black rectangles , "ERROR: Element 'parsererror' not yet implemented"
- Edge does not handle scaling and text-anchor:middle correctly in svg
- Property getBBox does not exist on type SVGElement
- Why does my d3 force-directed graph not display edges?
- Brushing on ordinal data does not work
- D3.js: time.scale.utc() does not seem to do anything for me?
- Renderer2 does not render SVG element while using d3 js - Angular 4
More Query from same tag
- nvd3 Bubble chart with log scale
- Blank screen with d3js
- x-value mouseover tooltip d3 - editing the tooltip style and text
- d3.js eyes following mouse blocks
- d3.js tooltips on path
- Arrow is not touching to nodes in d3.js tree view in mobile and touch screen
- Apply D3 tooltip to Donut Multiples
- d3 choropleth map is extremely small
- d3 5+ - Appending circles about a semi-circle or arc
- Unable to filter using html input value in D3JS
- Relative positioning of generated svg objects in D3
- Getting "TypeError: u.parentNode is undefined" when calling d3.sort or d3.order
- Confused about binding chord data to ribbon path
- Scroll bar should appear on zooming(scaling) svg image
- D3 axes render on top of graph
- Can I access bound data without D3?
- D3 Tree update color of nodes dynamically
- Prevent panning outside of map bounds in d3v5
- dc.js line chart - Date Value issue
- Update a d3.js (v5) selection bound with double nested data
- Filling D3.js Pie Graph with SQL Query
- Dual Y axis line chart in dc.js
- Transition chaining and cancellation
- dimple.js dash line chart
- How can I include a D3 (Scatterplot) Chart inside a SAPUI5 Control
- D3 General update pattern transition not working on pie chart
- Color paths in d3.layout.tree
- In D3, Bar graph's text labels are not updated after the inclusion of additional data
- D3.js path transition not working
- How to draw timeseries chart by month,year with d3js