Forked from joyplot /armollica/3b5f83836c1de5cca7b1d35409a013e3 and builds upon https://bl.ocks.org/tomshanley/66a8dc56756d94b1b05fbd8eb677b511
Built with blockbuilder.org
forked from tomshanley's block: So much joy :)
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="triangleEquations.js"></script>
<link href="https://fonts.googleapis.com/css?family=Share+Tech+Mono" rel="stylesheet">
<style>
body {
margin: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: DarkSlateBlue;
font-family: 'Share Tech Mono', monospace;
color: white
}
a {
color: white
}
g.tick>line {
stroke: lightgrey;
stroke-dasharray: 2, 2;
}
.axis-label, .series-label {
fill: lightgrey;
}
</style>
</head>
<body>
<h1>Joyplot + Wired Tweet Bots infographic</h1>
<p>An attempt at recreating the <a href="https://www.wired.com/2013/10/tweet-bots/">Wired infographic about tweet bots</a>, using the activity data from the <a href="https://bl.ocks.org/armollica/3b5f83836c1de5cca7b1d35409a013e3">"joy plot" example</a>.</p>
<script>
var noOfCharts;
var dataLength;
var xTickValues = []
let colour = d3.scaleSequential(d3.interpolatePlasma)
.domain([100, 0])
var formatTime = d3.timeFormat('%I %p');
const gradientColours = [
{ "offset": 0 },
{ "offset": 21 },
{ "offset": 60 },
{ "offset": 100 }
];
const xDiagonal = 393;
const zDiagonal = 713;
const yHeight = 50;
const xAngleDegrees = 25;
const xzAngleDegrees = 95 + xAngleDegrees;
const zAngleDegrees = 180 - xzAngleDegrees - xAngleDegrees;
const xzAngle = xzAngleDegrees * (Math.PI / 180);
const xAngle = xAngleDegrees * (Math.PI / 180); //between xDiagonal and horizontal
const zAngle = zAngleDegrees * (Math.PI / 180);
let xWidth = adjacentCos(xAngle, xDiagonal);
let zWidth = adjacentCos(zAngle, zDiagonal);
let xHeight = triangleSide(xWidth, xDiagonal);
let zHeight = triangleSide(zWidth, zDiagonal);
const margin = { "top": 50, "right": 150, "bottom": 50, "left": 150 };
const containerHeight = xHeight + zHeight + yHeight;
const containerWidth = xWidth + zWidth;
let xScale = d3.scaleTime()
.range([0, xWidth]);
let yScale = d3.scaleLinear()
//.domain([0, d3.max(data)])
.range([yHeight, 0]);
let activityScale = d3.scaleBand()
.range([0, zWidth]);
let zScale = d3.scaleLinear()
.domain([0, (noOfCharts - 1)])
.range([0, zWidth])
let area = d3.area()
.x(function (d, i) {
return xScale(d.time);
})
.y0(function (d, i) {
return yArea(0, i);
})
.y1(function (d, i) {
return yArea(d.value, i);
});
let svg = d3.select("body")
.append("svg")
.attr("width", containerWidth + margin.left + margin.right)
.attr("height", containerHeight + margin.top + margin.bottom);
let defs = svg.append("defs");
let gradient = defs.append("linearGradient")
.attr("id", "gradient")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", yHeight)
.attr("gradientUnits", "userSpaceOnUse")
.attr("gradientTransform", "rotate(-" + xAngleDegrees + ",0,0)")
gradient.selectAll("stop")
.data(gradientColours)
.enter()
.append("stop")
.attr("offset", function (d) { return d.offset + "%"; })
.attr("stop-color", function (d) { return colour(d.offset); });
let axes = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "axes")
let charts = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "charts");
d3.tsv('data.tsv', row, function (error, dataFlat) {
if (error) throw error;
// Sort by time
dataFlat.sort(function (a, b) { return a.time - b.time; });
var data = d3.nest()
.key(function (d) { return d.activity; })
.entries(dataFlat);
// Sort activities by peak activity time
function peakTime(d) {
var i = d3.scan(d.values, function (a, b) { return b.value - a.value; });
return d.values[i].time;
};
data.sort(function (a, b) { return peakTime(b) - peakTime(a); });
noOfCharts = data.length;
dataLength = data[0].values.length;
xScale.domain(d3.extent(dataFlat, function (d) { return d.time; }));
activityScale.domain(data.map(function (d) { return d.key; }));
yScale.domain(d3.extent(dataFlat, function (d) { return d.value }));
/*for (t = 0; t < dataLength; t++) {
xTickValues[t] = t;
};*/
xTickValues = [0,0.25,0.5,0.75,1]
axes.call(drawXAxis);
data.forEach(function (d, i) {
let series = i;
let activity = d.key;
let chartData = d.values;
let g = charts.append("g")
.attr("transform", "translate(" + areaOffsetX(series) + "," + areaOffsetY(series) + ")");
g.append("text")
.attr("class", "series-label")
.text(activity)
.attr("x", -8)
.attr("y", yHeight + 8)
.style("text-anchor", "end")
let areaChart = g.append("path")
.datum(chartData)
.style("fill", "url(#gradient"/* + series + ")"*/)
.attr("stroke", "DarkSlateBlue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.style("opacity", 0.8)
.attr("d", area)
.on("mouseover", function(d){
d3.selectAll("path, .series-label").style("opacity", 0.1)
d3.select(this).style("opacity", 1)
d3.select(this.parentNode).select(".series-label").style("opacity", 1)
})
.on("mouseout", function(d){
d3.selectAll("path").style("opacity", 0.8)
d3.selectAll(".series-label").style("opacity", 1)
})
})
})
function row(d) {
return {
activity: d.activity,
time: parseTime(d.time),
value: +d.p_smooth
};
};
function parseTime(offset) {
var date = new Date(2017, 0, 1); // chose an arbitrary day
return d3.timeMinute.offset(date, offset);
}
function areaOffsetX(i) {
return i * (zWidth / (noOfCharts - 1))
};
function areaOffsetY(i) {
let defaultY = containerHeight - zHeight - yHeight;
let offset = i * (zHeight / (noOfCharts - 1));
return defaultY + offset;
};
function yArea(d, i) {
let n = xHeight * (i / dataLength);
return yScale(d) - n;
};
function xCoord(x, y, z) {
let x1 = xScale(x);
let z1 = zScale(z);
return seriesWidth + xScale(x) - zScale(z)
};
function yCoord(x, y, z) {
let x1 = xHeight * (x / dataLength);
let y1 = chartHeight - yScale(y);
let z1 = zHeight * (z / noOfCharts);
return height - (y1 + x1 + z1)
};
function drawXAxis(sel) {
let xAxis = sel.append("g")
.attr("id", "x-axis")
.attr("transform", "translate(" + areaOffsetX(0) + "," + areaOffsetY(0) + ")");
let xTicks = xAxis.selectAll(".ticks")
.data(xTickValues)
.enter()
.append("g")
.attr("class", "tick")
.attr("transform", function (d) {
let x = xWidth * (d);
let y = yArea(0, (dataLength * (d)));
return "translate(" + x + "," + y + ")"
});
let tickLength = zDiagonal + 25;
let tickText = tickLength + 5;
xTicks.append("line")
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", adjacentCos(zAngle, tickLength))
.attr("y2", oppositeSin(zAngle, tickLength));
xTicks.append("text")
.attr("class", "axis-label")
.text(function (d) { return formatTime(xScale.invert(xWidth * (d))); })
.attr("x", adjacentCos(zAngle, tickText))
.attr("y", oppositeSin(zAngle, tickText));
};
</script>
</body>
https://d3js.org/d3.v4.min.js