Skip to content

Instantly share code, notes, and snippets.

@tomshanley
Last active August 4, 2017 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomshanley/66a8dc56756d94b1b05fbd8eb677b511 to your computer and use it in GitHub Desktop.
Save tomshanley/66a8dc56756d94b1b05fbd8eb677b511 to your computer and use it in GitHub Desktop.
Remake of Wired Twitter chart WIP
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="triangleEquations.js"></script>
<style>
body {
margin: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: DarkSlateBlue;
}
g.tick > line {
stroke: lightgrey;
stroke-dasharray: 2,2;
}
.axis-label {
fill: lightgrey;
}
</style>
</head>
<body>
<script>
const data = [0, 4, 1, 3, 4, 6, 3, 8, 11, 0, 2, 3, 3, 1, 8, 3, 0];
const noOfCharts = 24;
const dataLength = data.length;
const domain = [0, d3.max(data)];
let colour = d3.scaleSequential(d3.interpolatePlasma)
.domain([100,0])
let xTickValues = [];
for (t = 0; t < dataLength; t++) {
xTickValues[t] = t;
};
const gradientColours = [
{ "offset": 0},
{ "offset": 21},
{ "offset": 60},
{ "offset": 100}
];
const xDiagonal = 451;
const zDiagonal = 500;
const yHeight = 102;
const xAngleDegrees = 30;
const xzAngleDegrees = 90 + 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": 50, "bottom": 50, "left": 50 };
const containerHeight = xHeight + zHeight + yHeight;
const containerWidth = xWidth + zWidth;
let xScale = d3.scaleLinear()
.domain([0, (dataLength - 1)])
.range([0, xWidth]);
let yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([yHeight, 0]);
let zScale = d3.scaleLinear()
.domain([0, (noOfCharts-1)])
.range([0, zWidth])
let area = d3.area()
.x(function (d, i) {
return xScale(i);
})
.y0(function (d, i) {
return yArea(0, i);
})
.y1(function (d, i) {
return yArea(d, 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 axes = svg.append("g")
.attr("transform", "translate(" + margin.left + "," +margin.top + ")")
.attr("class", "axes")
.call(drawXAxis);
let charts = svg.append("g")
.attr("transform", "translate(" + margin.left + "," +margin.top + ")")
.attr("class", "charts");
for (var series = 0; series < noOfCharts; series++) {
let chartData = [];
data.forEach(function(d,i){
let change = Math.ceil(Math.random() * 3);
chartData[i] = data[i] + change;
});
let g = charts.append("g")
.attr("transform", "translate(" + areaOffsetX(series) + "," + areaOffsetY(series) + ")");
g.append("text")
.attr("class", "axis-label")
.text(series)
.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("fill-opacity", 0.9)
.attr("d", area);
let gradient = defs.append("linearGradient")
.attr("id", "gradient" + series)
.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); });
}; //end of for loop
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 = xScale(d);
let y = yArea(0, 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 d; })
.attr("x", adjacentCos(zAngle, tickText))
.attr("y", oppositeSin(zAngle, tickText));
};
</script>
</body>
//returns the length of the opposite side to the angle, using the adjacent side's length
function oppositeTan(angle, adjacent) {
return Math.tan(angle) * adjacent;
};
//returns the length of the adjacent side to the angle, using the hypotenuse's length
function adjacentCos(angle, hypotenuse) {
return Math.cos(angle) * hypotenuse;
}
//returns the length of the opposite side to the angle, using the adjacent's length
function oppositeTan(angle, adjacent) {
return Math.tan(angle) * adjacent;
}
//returns the length of the adjacent side to the angle, using the opposite's length
function adjacentTan(angle, opposite) {
return opposite / Math.tan(angle);
}
//returns the length of the opposite side to the angle, using the hypotenuse's length
function oppositeSin(angle, hypotenuse) {
return Math.sin(angle) * hypotenuse;
}
//returns the angle using the opposite and adjacent
function angleTan(opposite, adjacent) {
return Math.atan(opposite/adjacent);
}
//returns the length of the unknown side of a triangle, using the other two sides' lengths
function triangleSide(sideA, sideB) {
var hypothenuse, shorterSide;
if (sideA > sideB) {
hypothenuse = sideA;
shorterSide = sideB;
} else {
hypothenuse = sideB;
shorterSide = sideA;
};
return Math.sqrt(Math.pow(hypothenuse, 2) - Math.pow(shorterSide, 2))
};
//returns the length of the hypotenuse, using the other two sides' lengths
function triangleHypotenuse(sideA, sideB) {
return Math.sqrt(Math.pow(sideA, 2) + Math.pow(sideB, 2))
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment