Skip to content

Instantly share code, notes, and snippets.

@dankronstal
Last active February 5, 2016 22:02
Show Gist options
  • Save dankronstal/b12b025d0eea52ad3f4a to your computer and use it in GitHub Desktop.
Save dankronstal/b12b025d0eea52ad3f4a to your computer and use it in GitHub Desktop.
Arc Gradients

Dynamically generated gradients

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Arc Gradients</title>
<style>
</style>
</head>
<body>
<script src='https://d3js.org/d3.v3.min.js'></script>
<p>Click nodes to create links; ltr above, rtl below</p>
<script>
/*** start set up data ***/
// used to assign nodes color by group
var color = d3.scale.category20();
var graph = {
"nodes":[],
"links":[]
};
for(i=0; i<100; i++)
{
graph.nodes.push({"name":"node"+i, "id":i, "color":color(i)});
}
/*** end set up data ***/
var width = 1000; // width of svg image
var height = 800; // height of svg image
var margin = 20; // amount of margin around plot area
var pad = margin / 2; // actual padding amount
var radius = 5; // fixed node radius
var yfixed = height/2-margin;// pad + radius; // y position for all nodes
var newLink = null;
var tmpLink = null;
var popped;
var svg = d3.select("body")
.append("svg")
.attr("id", "arc")
.attr("width", width)
.attr("height", height);
var gradientDefs = svg.append("svg:defs");
// create plot area within svg image
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
// fix graph links to map to objects instead of indices
graph.links.forEach(function(d, i) {
d.source = isNaN(d.source) ? d.source : graph.nodes[d.source];
d.target = isNaN(d.target) ? d.target : graph.nodes[d.target];
});
// must be done AFTER links are fixed
linearLayout(graph.nodes);
// draw links first, so nodes appear on top
drawLinks(graph.links);
// draw nodes last
drawNodes(graph.nodes);
// Layout nodes linearly, sorted by group
function linearLayout(nodes) {
// used to scale node index to x position
var xscale = d3.scale.linear()
.domain([0, nodes.length - 1])
.range([radius, width - margin - radius]);
// calculate pixel location for each node
nodes.forEach(function(d, i) {
d.x = xscale(i);
d.y = yfixed;
});
}
// Draws nodes on plot
function drawNodes(nodes) {
d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", function(d, i) { return d.name; })
.attr("cx", function(d, i) { return d.x; })
.attr("cy", function(d, i) { return d.y; })
.attr("r", function(d, i) { return radius; })
.style("fill", function(d, i) { return d.color; })
.attr("pointer-events", "all")
.on("click", function(d){
if(newLink != null) {
newLink.target = d.id > newLink.source.id ? d : newLink.source;
newLink.source = d.id > newLink.source.id ? newLink.source : d;
newLink.torb= d.id == newLink.source.id ? 1 : -1;
newLink.value = 2;
newLink.fill = generateGradientFill(newLink.source,newLink.target);
graph.links.push(newLink);
drawLinks(graph.links);
d3.select("#"+newLink.source.name).style("fill",function(dd){ return d3.rgb(dd.color).darker(1);});
d3.select("#"+newLink.target.name).style("fill",function(dd){ return d3.rgb(dd.color).darker(1);});
newLink = null;
}else{
newLink = {};
newLink.source = d;
d3.select(this).style("fill",function(dd){ return d3.rgb(dd.color).brighter(1);});
}
})
.on("mousemove", function(d){
if(newLink != null) {
tmpLink = {source: newLink.source, target: d, value: 2, isTmp:1};
tmpLink.torb= d.id == tmpLink.source.id ? 1 : -1;
graph.links.push(tmpLink);
drawLinks(graph.links);
}
})
.on("mouseout", function(d){
tmpIndex = -1;
graph.links.forEach(function(d,i){
if(d.isTmp == 1) tmpIndex = i;
});
if(tmpIndex >=0){
graph.links.splice(tmpIndex,1);
drawLinks(graph.links);
}
});
}
// Draws nice arcs for each link on plot
function drawLinks(links) {
// scale to generate radians (just for lower-half of circle)
var radians = d3.scale.linear()
.range([Math.PI / 2, 3 * Math.PI / 2]);
// path generator for arcs (uses polar coordinates)
var arc = d3.svg.line.radial()
.interpolate("linear")
.tension(0)
.angle(function(d) { return radians(d); });
// add links
d3.select("#plot").selectAll(".link")
.data(links)
.enter()
.insert("path")
.attr("fill", "none")
.attr("stroke-width", function(d){ return d.value; })
.attr("stroke", function(d){ return d.fill; })
.attr("transform", function(d, i) {
// arc will always be drawn around (0, 0)
// shift so (0, 0) will be between source and target
var xshift = d.source.x + (d.target.x - d.source.x) / 2;
var yshift = yfixed;
return "translate(" + xshift + ", " + yshift + ")";
})
.attr("d", function(d, i) {
// get x distance between source and target
var xdist = Math.abs(d.source.x - d.target.x);
// set arc radius based on x distance
arc.radius(xdist / 2);
// want to generate 1/3 as many points per pixel in x direction
var points = d3.range(0, Math.ceil(xdist));
// set radian scale domain
//radians.domain([0, points.length - 1]); //orient arcs at bottom
radians.domain([0, d.torb * points.length - 1]);//orient arcs according to torb property
// return path for arc
return arc(points);
});
}
function generateGradientFill(s,t){
// Define the gradient
newGrad = gradientDefs.append("svg:linearGradient")
.attr("id", function(){ return "gradFor"+s.name+t.name; })
.attr("spreadMethod", "pad");
// Define the gradient color stops
newGrad.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", s.color)
.attr("stop-opacity", .75);
newGrad.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", t.color)
.attr("stop-opacity", .75);
return "url(#"+newGrad.attr("id")+")";
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment