Built with blockbuilder.org
Originally from charlesdguthrie/11356441
Updated to D3 v4 (thanks to StackOverflow for maximum speed :-)).
Many tweaks followed...
xxxxxxxxxx
<meta charset="utf-8">
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="everything">
<h1>Developer Interaction</h1>
<div id="chart"></div>
</div>
<script>
var setup = function(targetID){
//Set size of svg element and chart
const margin = {top: 0, right: 0, bottom: 0, left: 0},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom,
categoryIndent = 4*15 + 5,
defaultBarWidth = 2000;
//Set up scales
const x = d3.scaleLinear()
.domain([0,defaultBarWidth])
.range([0,width]),
y = d3.scaleBand().rangeRound([0, height]).padding(0.1);
const c = d3.scaleLinear()
.domain([10,190])
.range(['red','lightgreen'])
.interpolate(d3.interpolateHsl);
//Create SVG element
d3.select(targetID).selectAll("svg").remove()
const svg = d3.select(targetID).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 + ")");
//Package and export settings
const settings = {
margin:margin, width:width, height:height, categoryIndent:categoryIndent,
svg:svg, x:x, y:y, c:c
};
return settings;
}
var redrawChart = function(targetID, newdata) {
//Import settings
const { margin, width, height, categoryIndent, svg, x, y, c } = settings;
const deft = d3.transition().duration(300);
//Reset domains
y.domain(newdata.map(d => d.key));
x.domain([0,d3.max(newdata, e => e.value)]);
//Bind new data to chart rows
//Create chart row and move to below the bottom of the chart
const chartRow = svg.selectAll("g.chartRow")
.data(newdata, d => d.key);
//Fade out and remove exit elements
chartRow.exit()
.attr('class','chartRow')
.transition(deft)
.style("opacity","0")
.attr("transform", "translate(0," + (height + margin.top + margin.bottom) + ")")
.remove();
const newRow = chartRow
.enter()
.append("g")
.attr("class", d=> "chartRow")
.attr("transform", "translate(0," + height + margin.top + margin.bottom + ")");
//Add rectangles
newRow.insert("rect")
.attr("class","bar")
.attr("x", 0)
.attr("opacity",0)
.attr("height", y.bandwidth())
.attr("width", 0);
//Add value labels
newRow.append("text")
.attr("class","label")
.attr("y", y.bandwidth()/2)
.attr("x",0)
.attr("opacity",0)
.attr("dy",".35em")
.attr("dx","0.5em")
.text(d => "0");
//Add Headlines
newRow.append("text")
.attr("class","category")
.attr("text-overflow","ellipsis")
.attr("y", y.bandwidth()/2)
.attr("x",categoryIndent)
.attr("dy",".35em")
.attr("dx","0.5em")
.text(d => d.key);
const totRows = newRow.merge(chartRow);
//Update bar widths
totRows.select(".bar").transition()
.attr("width", d => x(d.value))
.attr("opacity",1)
.attr("height", y.bandwidth())
.attr("fill", d => c(d.value));
totRows.select(".category")
.transition()
.attr("y", y.bandwidth()/2);
//Update data labels
totRows.select(".label").transition().duration(700)
.attr("opacity",1)
.attr("y", y.bandwidth()/2)
.tween("text", function(d) {
var that = d3.select(this),
i = d3.interpolateNumber(that.text().replace(/,/g, ""),d.value);
return function(t) { that.text(Math.round(i(t))); };
});
////////////////
//REORDER ROWS//
////////////////
totRows.transition()
.delay((d, i)=> 100 + (15-i) * 30)
.duration(900)
.attr("transform",d => "translate(0," + y(d.key) + ")");
};
//Pulls data
//Since our data is fake, adds some random changes to simulate a data stream.
//Uses a callback because d3.json loading is asynchronous
var pullData = function(settings,callback){
d3.json("fakeData.json", function (err, data){
if (err) return console.warn(err);
let newData = data.news.map(d => {
const newValue = d.value + Math.floor((Math.random()*180) - 90);
return {key: d.key, value: newValue <= 0 ? 10 : newValue};
})
names = data.names.map(n => n.first_name);
cdata = cdata.slice(0,2+genRInt(cdata.length));
cdata = [...cdata.map(function(d) { return {key: d.key, value: clamp(d.value *(0.5+Math.random()),10,190)};}),...d3.zip(shfl(names),shfl(names)).slice(0,20)
.map(function([l,r]) {
return {
key: l + " - " + r,
value: 10+genRInt(100)
};})];
d3.shuffle(cdata);
newData = cdata.slice(0,5 + genRInt(10)).sort((a,b) => b.value-a.value); // formatData(newData);
callback(settings,newData);
})
}
const genRInt = (max) => Math.floor(Math.random() * max);
const clamp = (d,min,max) => Math.min(max,Math.max(min,d));
const shfl = (x) => d3.shuffle(x.slice());
//Sort data in descending order and take the top 10 values
var formatData = function(data){
return data.sort((a, b) => b.value - a.value)
.slice(0, 10);
}
//I like to call it what it does
var redraw = function(settings){
pullData(settings,redrawChart);
}
//setup (includes first draw)
var settings = setup('#chart');
redraw(settings);
//Repeat every 3 seconds
d3.interval(() => { redraw(settings); }, 2000);
var names, cdata = [];
</script>
</body>
Modified http://d3js.org/d3.v4.min.js to a secure url
https://d3js.org/d3.v4.min.js