I have made this list of countries, and I want its corresponding circle to have some interactivity, but the function declared for the mouseover and the mouseout are not working. Everything seems to be fine and I can't find the issue. Can you help me figure out what's happening?
here's my code
var mapData = [
{"code":"AU" , "name":"Australia", "value":3, "color":"#BC204B"},
{"code":"BE" , "name":"Belgium", "value":1, "color":"#BC204B"},
{"code":"BR" , "name":"Brazil", "value":1, "color":"#BC204B"},
{"code":"CA" , "name":"Canada", "value":4, "color":"#BC204B"},
{"code":"FI" , "name":"Finland", "value":1, "color":"#BC204B"},
{"code":"FR" , "name":"France", "value":1, "color":"#BC204B"},
{"code":"DE" , "name":"Germany", "value":1, "color":"#BC204B"},
{"code":"IS" , "name":"Iceland", "value":1, "color":"#BC204B"},
{"code":"IN" , "name":"India", "value":2, "color":"#BC204B"},
{"code":"IE" , "name":"Ireland", "value":1, "color":"#BC204B"},
{"code":"JP" , "name":"Japan", "value":2, "color":"#BC204B"},
{"code":"KP" , "name":"Korea, Dem. Rep.", "value":1, "color":"#BC204B"},
{"code":"LU" , "name":"Luxembourg", "value":1, "color":"#BC204B"},
{"code":"NL" , "name":"Netherlands", "value":3, "color":"#BC204B"},
{"code":"NZ" , "name":"New Zealand", "value":2, "color":"#BC204B"},
{"code":"NO" , "name":"Norway", "value":2, "color":"#BC204B"},
{"code":"PY" , "name":"Paraguay", "value":1, "color":"#BC204B"},
{"code":"SA" , "name":"Saudi Arabia", "value":2, "color":"#BC204B"},
{"code":"SG" , "name":"Singapore", "value":1, "color":"#BC204B"},
{"code":"ES" , "name":"Spain", "value":1, "color":"#BC204B"},
{"code":"CH" , "name":"Switzerland", "value":2, "color":"#BC204B"},
{"code":"AE" , "name":"United Arab Emirates", "value":2, "color":"#BC204B"},
{"code":"GB" , "name":"United Kingdom", "value":2, "color":"#BC204B"},
{"code":"US" , "name":"United States", "value":4, "color":"#BC204B"}
];
var width = "512"
var height = "400"
var radius = 5
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.style("background","#dedede")
.append("g")
.attr("transform", function (d, i) {
return "translate(" + width/20 + "," + (height -170) + ")";
});
var g = svg.selectAll("g")
.data(mapData)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" + i * 20 + "," + 0+ ")";
});
var circles = g.append("circle")
.attr("cx",0)
.attr("cy",0)
.attr("r",radius)
.attr("fill","#00A3A1")
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);
var labels = g.append("text")
.attr("class","countriesText")
.style("fill", "black")
.attr("x", 0)
.attr("y", 0)
.attr("dy", ".35em")
.attr("text-anchor", "start")
.text(function(d,i){
return mapData[i].name;
})
.attr("transform", "translate(0,20) scale(1) rotate(90)")
function handleMouseOver(d, i) {
d3.select(this).attr({
fill: "orange",
r: radius * 2
});
};
function handleMouseOut(d, i) {
d3.select(this).attr({
fill: "#00a3a1",
r: radius
})
};
*{
font-family: arial
-webkit-font-smoothing: antialiased;
}
.countriesText{
font-family: arial;
text-rendering: optimizeLegibility;
}
thanks
Related
I am Trying to display my full svg tree within the display in a center view.
var svg = d3.select("body").append("svg")
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform);
})).on("dblclick.zoom", null)
.attr("width", "1000")
.attr("height",590)
.append("g")
svg.attr("transform",function(d) {
return "translate(" + 450 + "," + svgHeight + ") scale(" + scale + ")";
} );
I am adjusting the scale and height properties of the transform depends on the number of nodes, actually its working fine.
The problem is when I am trying to zoom in/out on the tree for the first time , its not zooming the focused node.
This occurs only when I am doing zoom for first time, from second time its zooming the pointing node.
This is what I tried so far : Codepen
A drawback of the otherwise solid solution #soundquiet posted is that it disrupts panning behaviour, leading to some sort of rapid shifting of the nodes.
A simpler, and more robust solution is shown below. I just wrap the g-element inside another g element called zoomContainer and call all zoom behaviour on that instead.
var treeData = {
"name": "Share point Server 2019",
"id": "a093F0000078Id5QAE",
"children": [{
"name": "is extended by",
"level": "sub node",
"children": [{
"name": "Share point Server 2019",
"id": "a093F0000078Id5QAE"
}]
}, {
"name": "manages",
"level": "sub node",
"children": [{
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}, {
"name": "HPE ProLiant ML350 Tower",
"id": "a093F0000078IcHQAU"
}]
}, {
"name": "is operated by",
"level": "sub node",
"children": [{
"name": "Power point SH-20",
"id": "a093F00000794ZWQAY"
}]
}]
};
var scale = 1,
svgHeight = 200,
nodeCount = 13;;
var margin = {
top: 20,
right: 90,
bottom: 30,
left: 90
},
width = window.outerWidth,
height = window.outerHeight;
var focused = false;
console.log('scale', scale)
var zoomContainer = d3.select("body").append("svg")
.call(d3.zoom().on("zoom", function() {
zoomContainer.attr("transform", d3.event.transform);
})).on("dblclick.zoom", null)
.attr("width", "1000")
.attr("height", 590)
.append("g")
var svg = zoomContainer
.append("g")
.attr("transform", function(d) {
return "translate(" +
(450) + "," + (svgHeight) + ") scale(" + scale + ")";
});
var i = 0,
duration = 750,
root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) {
return d.children;
});
root.x0 = height;
root.y0 = 0;
// Collapse after the second level
if (typeof collapse === 'undefined')
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
let left = root;
let right = root;
var dx = ((nodeCount * 18) / 1000);
// Normalize for fixed-depth.
nodes.forEach(function(d, index) {
d.y = d.depth * 180;
d.x = d.x * ((nodeCount * 18) / 1000);
});
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new modes at the parent's previous position.
var nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr("transform", function(d) {
return "translate(" + (source.y0) + "," + (source.x0) + ")";
})
.on('click', d => {
// d3.event.preventDefault();
component.set("v.nodeName", d.data.name);
})
.on("dblclick", click);
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style('stroke', 'steelblue')
.style('stroke-width', '3px');
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.data.name;
});
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + (d.y) + "," + (d.x) + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 3)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + (source.y) + "," + source.x + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) {
return d.id;
});
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.style('fill', "none")
.style('stroke', "#ccc")
.style('stroke-width', "2px")
.attr('d', function(d) {
var o = {
x: source.x0,
y: source.y0
}
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d) {
return diagonal(d, d.parent)
});
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {
x: source.x,
y: source.y
}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
var path = "M" + d.y + "," + d.x +
"C" + (d.y + s.y) / 2 + "," + d.x +
" " + (d.y + s.y) / 2 + "," + s.x +
" " + s.y + "," + s.x;
return path;
}
// Toggle children on click.
function click(d) {
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div style="background:white" id="body"></div>
I designed a codepan which uses D3 V4 Tree layout. First left click anywhere on svg calling zoomed function and applying translate(0,0) scale(1) to tag "points_g" (line 595 in codepan). this shifting creates discrepancy between x-scale values and node values.(non-leaf node values represent x-axis values).
Why is it shifting svg to translate(0,0) scale(1) on first left click without even without mouse or touchpad movement.
https://codepen.io/ankit-jo14/pen/qQXweG
React here is just calling this.createcluster(). All D3 stuff/render is done by createcluster method only.
/*
* A simple React component
*/
let data = {
"nodeName": "node3",
"parentName": null,
"height": 0.9855515016920474,
"children": [{
"nodeName": "PI101404A",
"parentName": "node3",
"height": 0,
"children": null
}, {
"nodeName": "node2",
"parentName": "node3",
"height": 0.7673460120162033,
"children": [{
"nodeName": "PI103079",
"parentName": "node2",
"height": 0,
"children": null
}, {
"nodeName": "node1",
"parentName": "node2",
"height": 0.6499660584648042,
"children": [{
"nodeName": "PI123440",
"parentName": "node1",
"height": 0,
"children": null
}, {
"nodeName": "node0",
"parentName": "node1",
"height": 0.3968508997429306,
"children": [{
"nodeName": "PI124871",
"parentName": "node0",
"height": 0,
"children": null
}, {
"nodeName": "PI131531",
"parentName": "node0",
"height": 0,
"children": null
}]
}]
}]
}]
};
class HCluster extends React.Component {
constructor(props) {
super(props);
// Don't call this.setState() here!
this.state = {
svgwidth: 1000,
svgheight: 1000
};
}
componentDidMount() {
this.createcluster();
}
componentDidUpdate() {
this.createcluster();
}
createcluster() {
let pcamargin = {
top: 20,
right: 150,
bottom: 20,
left: 30
};
let width = this.state.svgwidth;
let height = this.state.svgheight - 20;
let root = d3.hierarchy(this.props.data);
// height and width are opposite of dim as it 90 degree roteted.
var tree = d3
.cluster()
.size([height - pcamargin.bottom, width - pcamargin.right]);
tree(root);
console.log(root);
let cluster = d3.select(this.refs.anchor);
cluster.selectAll("*").remove();
cluster.attr("width", this.state.svgwidth);
cluster.attr("height", this.state.svgheight);
// create a clipping region
cluster
.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
// create scale objects
let xScale = d3
.scaleLinear()
.domain([0, 1])
.range([width - pcamargin.left, 0]);
// create axis objects
let xAxis = d3.axisBottom(xScale).ticks(20, "s");
// Draw Axis
let gX = cluster
.append("g")
.attr(
"transform",
"translate(" +
pcamargin.left +
"," +
(this.state.svgheight - pcamargin.bottom) +
")"
)
.call(xAxis);
let zoom = d3
.zoom()
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
cluster
.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr(
"transform",
"translate(" + pcamargin.left + "," + pcamargin.top + ")"
)
.call(zoom);
// Draw Datapoints
var points_g = cluster
.append("g")
.attr(
"transform",
"translate(" + pcamargin.left + "," + pcamargin.top + ")"
)
.attr("clip-path", "url(#clip)")
.classed("points_g", true);
let links = points_g
.selectAll(".link")
.data(root.descendants().slice(1))
.enter()
.append("path");
links.each(function (d) {
// console.log("d.y before " +d.data.name +" "+ d.y + " height" + d.data.height );
//d.y = d.depth * 120 + (d.children ? d.data.height : height - 160 );
//d.y = (height/2) - d.data.height;
return (d.y = xScale(d.data.height));
// console.log("d.y after " + d.y);
});
links.attr("class", "link").attr("d", function (d) {
return (
"M" +
d.y +
"," +
d.x +
"L" +
d.parent.y +
"," +
d.x +
" " +
d.parent.y +
"," +
d.parent.x
);
});
let nodes = points_g
.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("class", function (d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function (d) {
return (
"rotate( 0 " +
d.y +
"," +
d.x +
")" +
" translate(" +
d.y +
"," +
d.x +
")"
);
});
nodes.append("circle").attr("r", 2.5);
nodes
.append("text")
.attr("x", 10)
.attr("y", 2)
.text(function (d) {
return d.children ? d.data.height : d.data.name;
});
// width = +svg.attr("width");
// height = +svg.attr("height");
// var g = svg.append("g").attr("transform", "translate(40,0)");
function zoomed() {
console.log("zoomcalled");
var new_xScale = d3.event.transform.rescaleX(xScale);
points_g.attr("transform", d3.event.transform);
gX.call(xAxis.scale(new_xScale));
}
}
render() {
return <svg ref="anchor" />;
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(
<HCluster data={data} />,
document.getElementById("app")
);
I want to link circles and rects by the shapes' name, it's all right.
I also remove the force layout, cause I want to use the static position.
but the drag function doesn't work, only the link can be dragged, not the Shapes. I dont konw the reason.
does someon help me review the code?
Thanks you very much.
var graph = {
"nodes":[
{
"appId":"AP110358",
"name":"Customer Account Profile",
"type":"CIRCLE",
"x":50,
"y":50
},
{
"appId":"NB",
"name":"NB",
"type":"CIRCLE",
"x":500,
"y":500
},
{
"appId":"AP114737",
"name":"RBG",
"type":"CIRCLE",
"x":300,
"y":600
},
{
"appId":"NULL",
"name":"Account",
"type":"RECT",
"x":400,
"y":700
}
],
"links":[
{
"source":"Customer Account Profile",
"target":"NB",
"value":1,
"label":null
},
{
"source":"NB",
"target":"RBG",
"value":1,
"label":null
},
{
"source":"RBG",
"target":"Customer Account Profile",
"value":1,
"label":null
},
{
"source":"NB",
"target":"Customer Account Profile",
"value":1,
"label":null
},
{
"source":"Customer Account Profile",
"target":"Account",
"value":1,
"label":null
},
{
"source":"NB",
"target":"Account",
"value":1,
"label":null
},
{
"source":"RBG",
"target":"Account",
"value":1,
"label":null
}
]
};
var width = window.innerWidth;
var height = window.innerHeight;
var center;
if(width > 1200){
center = [(width-1200) / 2, 0]
}else{
center = [0, 0]
}
var edges = [];
graph.links.forEach(function(e) {
var sourceNode = graph.nodes.filter(function(n) {
return n.name === e.source;
})[0],
targetNode = graph.nodes.filter(function(n) {
return n.name === e.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
value: e.value
});
});
var color = d3.scale.category20();
var svg = d3.select("body")
.append("svg")
.attr("width", "1200")
.attr("height", "1000")
.attr("transform", "translate(" + center + ")")
.append("g");
var container = svg.append("g");
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(l) {
var sourceNode = graph.nodes.filter(function(d) {
return d == l.source
})[0];
d3.select(this).attr("y1", sourceNode.y);
return sourceNode.x
})
.attr("x2", function(l) {
var targetNode = graph.nodes.filter(function(d) {
return d == l.target
})[0];
d3.select(this).attr("y2", targetNode.y);
return targetNode.x
})
.style("stroke-width", function(d) {
return d.value;
});
link.append("title").text(function(d) {
return d.value;
});
var drag = d3.behavior.drag()
.on("drag", function(d, i) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
link.each(function(l) {
if (l.source == d) {
d3.select(this).attr("x1", d.x).attr("y1", d.y);
} else if (l.target == d) {
d3.select(this).attr("x2", d.x).attr("y2", d.y);
}
});
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(drag);
var i = 0;
node.each(function(d) {
if (d.type == "CIRCLE") {
d3.select(this).append("circle")
.attr("r", 50)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.style("fill", function(d) { return color(i & 3);});
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y+5) + ")";
})
.style("text-anchor","middle");
} else {
d3.select(this).append("rect")
.attr("height", 40)
.attr("width", 140)
.attr("x", -10)
.attr("y", -(40 / 2))
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y+5) + ")";
})
.style("fill", "green");
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.style("text-anchor","start")
.attr("transform", function(d) {
return "translate(" + (d.x - 5) + "," + (d.y+10) + ")";
});
}
i++;
});
node.on("click", function(d){
console.log(d.x + "|--|" + d.y);
});
<style type="text/css">
.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body style="background-color: #42f4e5">
<!-- main -->
</body>
I know the reason
I call the drag method in the node, but the node doesn't append the shape, so the drag doesn't work.
so I change the drag invoke position as snippet
var graph = {
"nodes": [{
"appId": "AP110358",
"name": "Customer Account Profile",
"type": "CIRCLE",
"x": 50,
"y": 50
},
{
"appId": "NB",
"name": "NB",
"type": "CIRCLE",
"x": 500,
"y": 500
},
{
"appId": "AP114737",
"name": "RBG",
"type": "CIRCLE",
"x": 300,
"y": 600
},
{
"appId": "NULL",
"name": "Account",
"type": "RECT",
"x": 400,
"y": 700
}
],
"links": [{
"source": "Customer Account Profile",
"target": "NB",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "RBG",
"value": 1,
"label": null
},
{
"source": "RBG",
"target": "Customer Account Profile",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "Customer Account Profile",
"value": 1,
"label": null
},
{
"source": "Customer Account Profile",
"target": "Account",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "Account",
"value": 1,
"label": null
},
{
"source": "RBG",
"target": "Account",
"value": 1,
"label": null
}
]
};
var width = window.innerWidth;
var height = window.innerHeight;
var center;
if (width > 1200) {
center = [(width - 1200) / 2, 0]
} else {
center = [0, 0]
}
var edges = [];
graph.links.forEach(function(e) {
var sourceNode = graph.nodes.filter(function(n) {
return n.name === e.source;
})[0],
targetNode = graph.nodes.filter(function(n) {
return n.name === e.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
value: e.value
});
});
var color = d3.scale.category20();
var svg = d3.select("body")
.append("svg")
.attr("width", "1200")
.attr("height", "1000")
.attr("transform", "translate(" + center + ")")
.append("g");
var container = svg.append("g");
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(l) {
var sourceNode = graph.nodes.filter(function(d) {
return d == l.source
})[0];
d3.select(this).attr("y1", sourceNode.y);
return sourceNode.x
})
.attr("x2", function(l) {
var targetNode = graph.nodes.filter(function(d) {
return d == l.target
})[0];
d3.select(this).attr("y2", targetNode.y);
return targetNode.x
})
.style("stroke-width", function(d) {
return d.value;
});
link.append("title").text(function(d) {
return d.value;
});
var drag = d3.behavior.drag()
.on("drag", function(d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
link.each(function(l) {
if (l.source == d) {
d3.select(this).attr("x1", d.x).attr("y1", d.y);
} else if (l.target == d) {
d3.select(this).attr("x2", d.x).attr("y2", d.y);
}
});
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node");
var i = 0;
node.each(function(d) {
if (d.type == "CIRCLE") {
d3.select(this).append("circle")
.attr("r", 50)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.call(drag)
.style("fill", function(d) {
return color(i & 3);
});
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y + 5) + ")";
})
.style("text-anchor", "middle");
} else {
d3.select(this).append("rect")
.attr("height", 40)
.attr("width", 140)
.attr("x", -10)
.attr("y", -(40 / 2))
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y + 5) + ")";
})
.style("fill", "green");
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.style("text-anchor", "start")
.attr("transform", function(d) {
return "translate(" + (d.x - 5) + "," + (d.y + 10) + ")";
});
}
i++;
});
node.on("click", function(d) {
console.log(d.x + "|--|" + d.y);
});
<style type="text/css">.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.1/d3.min.js"></script>
<body style="background-color: #42f4e5">
<!-- main -->
</body>
I am new to d3 and I am trying to create a collapsible version of https://bl.ocks.org/mbostock/4063550
in v4. There are various examples that explain the same in v3, but I couldn't find a proper one for v4.
I implemented the onClick function in which the children opens up, but the issue is that the links get misplaced after the click. they shift to the right, and the nodes remain at the same position.
Please find below my code for the update function:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
<!--
.node circle {
//fill: #999;
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 10px sans-serif;
}
.node--internal circle {
fill: #555;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
-->
.node {
cursor: pointer;
}
.node circle {
fill: #999;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
</style>
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 1000,
duration = 750;
var nodes,links;
var i = 0;
var svg = d3.select("body").append("svg")
.attr("width",width)
.attr("height",height);
var g = svg.append("g").attr("transform", "translate(" + (width / 2 + 40) + "," + (height / 2 + 90) + ")");
function connector(d) {
return "M" + project(d.x, d.y)
+ "C" + project(d.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, d.parent.y)
/*
return "M" + d.y + "," + d.x +
"C" + (d.y + d.parent.y) / 2 + "," + d.x +
" " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
" " + d.parent.y + "," + d.parent.x; */
}
var treeMap = d3.tree()
.size([360,250]),
root;
var nodeSvg, linkSvg, nodeEnter, linkEnter ;
d3.json("treeData.json",function(error,treeData){
if(error) throw error;
root = d3.hierarchy(treeData,function(d){
return d.children;
});
root.each(function (d) {
console.log(d);
d.name = d.data.name; //transferring name to a name variable
d.id = i; //Assigning numerical Ids
i += i;
});
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
//root.children.forEach(collapse);
update(root);
});
function update(source) {
//root = treeMap(root);
nodes = treeMap(root).descendants();
//console.log(nodes);
//links = root.descendants().slice(1);
links = nodes.slice(1);
//console.log(links);
var nodeUpdate;
var nodeExit;
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
nodeSvg = g.selectAll(".node")
.data(nodes,function(d) { return d.id || (d.id = ++i); });
//nodeSvg.exit().remove();
var nodeEnter = nodeSvg.enter()
.append("g")
//.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + project(d.x, d.y) + ")"; })
//.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click",click)
.on("mouseover", function(d) { return "minu"; });
nodeEnter.append("circle")
.attr("r", 5)
.style("fill", color);
nodeEnter.append("text")
.attr("dy", ".31em")
//.attr("x", function(d) { return d.x < 180 === !d.children ? 6 : -6; })
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.style("text-anchor", function(d) { return d.x < 180 === !d.children ? "start" : "end"; })
//.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.attr("transform", function(d) { return "rotate(" + (d.x < 180 ? d.x - 90 : d.x + 90) + ")"; })
.text(function(d) { return d.data.name; });
// Transition nodes to their new position.
var nodeUpdate = nodeSvg.merge(nodeEnter).transition()
.duration(duration);
// .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeSvg.select("circle")
.style("fill", color);
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = nodeSvg.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) //for the animation to either go off there itself or come to centre
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
linkSvg = g.selectAll(".link")
.data(links, function(link) { var id = link.id + '->' + link.parent.id; return id; });
// Transition links to their new position.
linkSvg.transition()
.duration(duration);
// .attr('d', connector);
// Enter any new links at the parent's previous position.
linkEnter = linkSvg.enter().insert('path', 'g')
.attr("class", "link")
.attr("d", function(d) {
return "M" + project(d.x, d.y)
+ "C" + project(d.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, d.parent.y);
});
/*
function (d) {
var o = {x: source.x0, y: source.y0, parent: {x: source.x0, y: source.y0}};
return connector(o);
});*/
// Transition links to their new position.
linkSvg.merge(linkEnter).transition()
.duration(duration)
.attr("d", connector);
// Transition exiting nodes to the parent's new position.
linkSvg.exit().transition()
.duration(duration)
.attr("d", /*function (d) {
var o = {x: source.x, y: source.y, parent: {x: source.x, y: source.y}};
return connector(o);
})*/function(d) {
return "M" + project(d.x, d.y)
+ "C" + project(d.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, d.parent.y);
})
.remove();
// Stash the old positions for transition.
}
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
function color(d) {
return d._children ? "#3182bd" // collapsed package
: d.children ? "#c6dbef" // expanded package
: "#fd8d3c"; // leaf node
}
function flatten (root) {
// hierarchical data to flat data for force layout
var nodes = [];
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
else ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
function project(x, y) {
var angle = (x - 90) / 180 * Math.PI, radius = y;
return [radius * Math.cos(angle), radius * Math.sin(angle)];
}
</script>
</body>
</html>
treeData.json:
{
"name": "United States",
"children": [
{
"name": "Arizona",
"children":[
{ "name" : "Arizona Airport", "size": 13}
]
},
{
"name": "California",
"children":[
{"name": "San Francisco","size":15},
{"name": "San Jose","size":25},
{"name": "Los Angeles","size":17}
]
},
{
"name": "Illinois",
"children":[
{ "name" : "Chicago O'Hare", "size": 13},
{ "name" : "Midway", "size": 18 }
]
},
{
"name": "Colorado",
"children" : [
{ "name": "Denver","size": 7}
]
},
{
"name": "Florida","size":2
},
{
"name": "Georgia", "size": 25
},
{
"name": "Kentucky","size":2
},
{
"name": "Massachussets", "size": 25
},
{
"name": "Michigan","size":2
},
{
"name": "Minnesota", "size": 25
},
{
"name": "Missouri","size":2
},
{
"name": "North Carolina", "size": 25
},
{
"name": "Nevada","size":2
},
{
"name": "Newyork", "size": 12
},
{
"name": "Oregon","size":2
},
{
"name": "Pennsylvania", "size": 25
},
{
"name": "Washington",
"children": [
{ "name" : "Seattle","size" : 13}
]
},
{
"name": "Hawaii", "size": 25
},
{
"name": "Texas",
"children" : [
{ "name": "Dallas" ,"size": 9},
{ "name": "Houston" ,"size": 13},
{ "name": "Austin" ,"size": 17}
]
},
{
"name": "Utah", "size": 25
},
{
"name": "Virginia", "size": 25
}
]
}
#Minu didn't update the code in the question, so here's what I did to get it to work:
Just change:
// Transition nodes to their new position.
var nodeUpdate = nodeSvg.merge(nodeEnter).transition()
.duration(duration);
to this:
// Transition nodes to their new position.
var nodeUpdate = nodeSvg.merge(nodeEnter).transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + project(d.x, d.y) + ")"; });
Hopefully this is helpful to someone else who wants to make the radial tree collapsible.
My goal is making a multiple line of line drawing. It is all working fine other than adding the circles on the curves.
Below is the code snippets. HTML and CSS also added below. Can anyone please help me to fix this?
window.onload = function () {
var datas = [
{"date":20111001,"New York" : 63.4, "San Franscisco" : 62.7, "Austin" : 72.2 },
{"date":20111002,"New York" : 58.0, "San Franscisco" : 59.9, "Austin" : 67.7 },
{"date":20111003,"New York" : 53.3, "San Franscisco" : 59.1, "Austin" : 69.4 },
{"date":20111004,"New York" : 55.7, "San Franscisco" : 58.8, "Austin" : 68.0 },
{"date":20111005,"New York" : 64.2, "San Franscisco" : 58.7, "Austin" : 72.4 },
{"date":20111006,"New York" : 58.8, "San Franscisco" : 57.0, "Austin" : 77.0 },
{"date":20111007,"New York" : 57.9, "San Franscisco" : 56.7, "Austin" : 82.3 },
{"date":20111008,"New York" : 61.8, "San Franscisco" : 56.8, "Austin" : 78.9 },
{"date":20111009,"New York" : 69.3, "San Franscisco" : 56.7, "Austin" : 68.8 },
{"date":20111010,"New York" : 71.2, "San Franscisco" : 60.1, "Austin" : 68.7 }
]
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal()
.domain(["New York", "San Francisco", "Austin"])
.range(["#FF0000", "#009933" , "#0000FF"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(datas[0]).filter(function(key) { return key !== "date"; }));
datas.forEach(function(d) {
d.date = parseDate(d.date + "");
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: datas.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
x.domain(d3.extent(datas, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 6)
// .attr("dy", ".71em")
// .style("text-anchor", "end")
// .text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
var path = city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
var totalLength = [path[0][0].getTotalLength()];
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
city.selectAll("circle.line")
.data(cities)
.enter().append("svg:circle")
.attr("class", "line")
.attr("stroke-width", 2)
.attr("stroke", "green")
.attr("fill", "transparent")
.attr("cx", function(d,i){ return line(d.values) })
.attr("cy", function(d,i){ return line(d.values) })
.attr("r", 3.5);
// city.append("text")
// .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
// .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
// .attr("x", 3)
// .attr("dy", ".35em")
// .text(function(d) { return d.name; });
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Try this way.
city.selectAll("circle")
.data(function(d) {
return d.values
})
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.temperature)
})
.attr("stroke-width", 2)
.attr("stroke", "green")
.attr("fill", "transparent");
Note: I have commented the line interpolation, since it is not possible to find the exact point positions in that case. Reference: points on line in d3.js
window.onload = function() {
var datas = [{
"date": 20111001,
"New York": 63.4,
"San Franscisco": 62.7,
"Austin": 72.2
}, {
"date": 20111002,
"New York": 58.0,
"San Franscisco": 59.9,
"Austin": 67.7
}, {
"date": 20111003,
"New York": 53.3,
"San Franscisco": 59.1,
"Austin": 69.4
}, {
"date": 20111004,
"New York": 55.7,
"San Franscisco": 58.8,
"Austin": 68.0
}, {
"date": 20111005,
"New York": 64.2,
"San Franscisco": 58.7,
"Austin": 72.4
}, {
"date": 20111006,
"New York": 58.8,
"San Franscisco": 57.0,
"Austin": 77.0
}, {
"date": 20111007,
"New York": 57.9,
"San Franscisco": 56.7,
"Austin": 82.3
}, {
"date": 20111008,
"New York": 61.8,
"San Franscisco": 56.8,
"Austin": 78.9
}, {
"date": 20111009,
"New York": 69.3,
"San Franscisco": 56.7,
"Austin": 68.8
}, {
"date": 20111010,
"New York": 71.2,
"San Franscisco": 60.1,
"Austin": 68.7
}
]
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal()
.domain(["New York", "San Francisco", "Austin"])
.range(["#FF0000", "#009933", "#0000FF"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
// .interpolate("basis")
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.temperature);
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(datas[0]).filter(function(key) {
return key !== "date";
}));
datas.forEach(function(d) {
d.date = parseDate(d.date + "");
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: datas.map(function(d) {
return {
date: d.date,
temperature: +d[name]
};
})
};
});
x.domain(d3.extent(datas, function(d) {
return d.date;
}));
y.domain([
d3.min(cities, function(c) {
return d3.min(c.values, function(v) {
return v.temperature;
});
}),
d3.max(cities, function(c) {
return d3.max(c.values, function(v) {
return v.temperature;
});
})
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 6)
// .attr("dy", ".71em")
// .style("text-anchor", "end")
// .text("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
var path = city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
var totalLength = [path[0][0].getTotalLength()];
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
city.selectAll("circle")
.data(function(d) {
return d.values
})
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.date)
})
.attr("cy", function(d) {
return y(d.temperature)
})
.attr("r", 3.5)
.attr("fill", "transparent")
.attr("stroke", "green")
.attr("stroke-width", 0)
.transition()
.duration(2000)
.attr("stroke-width", 2)
// city.append("text")
// .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
// .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
// .attr("x", 3)
// .attr("dy", ".35em")
// .text(function(d) { return d.name; });
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>