I am now to d3 and web development in general.
I am creating a graph using the d3 library. I am trying to ensure that whenever the user hovers upon a node, the opacity of its immediate parents and children should remain the same but the opacity of the rest of the nodes should decrease.
I am partly achieving my goal by letting the text written below fade for all the ones other than the one I hover on.
Here is my javascript code:
// setting up the canvas size :)
var width = 960,
height = 500;
// initialization
var svg = d3.select("div").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id", "blueLine"); // the graph invisible thing :)
var force = d3.layout.force()
.gravity(0) // atom's cohesiveness / elasticity of imgs :)
.distance(150) // how far the lines ---> arrows :)
.charge(-50) // meta state transition excitement
.linkDistance(140)
//.friction(0.55) // similar to charge for quick reset :)
.size([width, height]); // degree of freedom to the canvas
// exception handling
d3.json("graph.json", function(error, json) {
if (error) throw error;
// Restart the force layout
force
.nodes(json.nodes)
.links(json.links)
.start();
// Build the link
var link = svg.selectAll(".links")
.data(json.links)
.enter().append("line")
.attr("class", "lol")
.style("stroke-width", "2")
.attr("stroke", function(d){
return linkColor(d.colorCode);})
.each(function(d) {
var color = linkColor(d.colorCode);
d3.select(this).attr("marker-end", marker(color));
});
function marker(color) {
svg.append("svg:marker")
.attr("id", color.replace("#", ""))
.attr("viewBox", "0 -5 10 10")
.attr("refX", 10)
.attr("refY", 0)
.attr("markerWidth", 15)
.attr("markerHeight", 15)
.attr("orient", "auto")
.attr("markerUnits", "userSpaceOnUse")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("fill", color);
return "url(" + color + ")";
};
// this link : https://stackoverflow.com/questions/32964457/match-arrowhead-color-to-line-color-in-d3
// create a node
var node = svg.selectAll(".nodes")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag)
.on("mouseover", fade(.2))
.on("mouseout", fade(1));;
// Define the div for the tooltip
var div = d3.select("body").append("pre")
.attr("class", "tooltip")
.style("opacity", 0);
// Append custom images
node.append("svg:image")
.attr("xlink:href", function(d) { return d.img;}) // update the node with the image
.attr("x", function(d) { return -5;}) // how far is the image from the link??
.attr("y", function(d) { return -25;}) // --- same ---
.attr("height", 55) // size
.attr("width", 55);
node.append("text")
.attr("class", "labelText")
.attr("x", function(d) { return -5;})
.attr("y", function(d) { return 48;})
.text(function(d) { return d.name });
force.on("tick", function() {
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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
force.stop();
});
function linkColor(linkCode) {
switch (linkCode)
{
case 'ctoc':
return '#0000FF';//blue
break;
case 'ctof':
return '#00afaa';//green
break;
case 'ftoc':
return '#fab800';//yellow
break;
case 'ftof':
return '#7F007F';//purple
break;
default:
return '#0950D0';//generic blue
break;
}
}
// build a dictionary of nodes that are linked
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.id + "," + d.target.id] = 1;
});
// check the dictionary to see if nodes are linked
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
// fade nodes on hover
function fade(opacity) {
return function(d) {
// check all other nodes to see if they're connected
// to this one. if so, keep the opacity at 1, otherwise
// fade
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
return thisOpacity;
});
node.style("fill-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
return thisOpacity;
});
// also style link accordingly
link.style("stroke-opacity", function(o) {
return o[0] === d || o[2] === d ? 1 : opacity;
});
};
}
});
The css:
.node text {
font-size: 1rem;
text-decoration: underline;
fill: #aeb4bf;
font-weight: 700;
text-anchor: end;
alignment-baseline: central;
pointer-events: none;
}
.node:not(:hover) .nodetext {
display: none;
}
pre.tooltip {
position: absolute;
text-align: left;
width: auto;
height: auto;
padding: 5px;
font: 14px "Helvetica","Arial",sans-serif bold;
background: #273142;
border: 0;
border-radius: 8px;
cursor: pointer!important;
pointer-events: none;
color: #aeb4bf;
}
and my json file:
{
"nodes": [
{"x": 100, "y": 100, "name": "A", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 0},
{"x": 250, "y": 100, "name": "B", "img":"https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 1},
{"x": 400, "y": 100, "name": "C", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id": 2},
{"x": 550, "y": 200, "name": "D", "img":"https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 3},
{"x": 700, "y": 200, "name": "E", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 4},
{"x": 100, "y": 300, "name": "F", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 5},
{"x": 250, "y": 300, "name": "G", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 6},
{"x": 400, "y": 300, "name": "H", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id": 7}
],
"links": [
{"source": 0, "target": 1, "colorCode" : "ctof"},
{"source": 1, "target": 2, "colorCode" : "ftoc"},
{"source": 2, "target": 3, "colorCode" : "ctof"},
{"source": 3, "target": 4, "colorCode" : "ftoc"},
{"source": 5, "target": 6, "colorCode" : "ctof"},
{"source": 6, "target": 7, "colorCode" : "ftoc"},
{"source": 7, "target": 3, "colorCode" : "ctof"}
]
}
I dont know where I am going wrong. I need to achieve two things: 1. The immediate parents and children of X should stay unfaded if I hover over X and 2. The other nodes which aren't directly related to X should fade just the way the other links do. Currently none of the node fades.
I researched over my code and realized that it says that all the nodes are connected to each other so my isConnected()
is the culprit. I still have no clue about the links though.
Please help me.