A development on bunkat's timeline in d3.js.
Last active
August 19, 2016 12:49
-
-
Save guypursey/620adb6185d7a6446f1ab34f9b579a1d to your computer and use it in GitHub Desktop.
Tweaked timeline in D3.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | |
<title>Chronological Diagram of Asia</title> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script> | |
<style type="text/css"> | |
.chart { | |
shape-rendering: crispEdges; | |
} | |
.mini text { | |
font: 9px sans-serif; | |
} | |
.main text { | |
font: 12px sans-serif; | |
} | |
.miniItem0 { | |
fill: darksalmon; | |
stroke-width: 6; | |
} | |
.miniItem1 { | |
fill: darkolivegreen; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.miniItem2 { | |
fill: slategray; | |
fill-opacity: .7; | |
stroke-width: 6; | |
} | |
.brush .extent { | |
stroke: gray; | |
fill: dodgerblue; | |
fill-opacity: .365; | |
} | |
.axis { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
//data | |
var lanes = ["Chinese","Japanese","Korean"], | |
laneLength = lanes.length, | |
items = [{"lane": 0, "id": "Qin", "start": 5, "end": 205}, | |
{"lane": 0, "id": "Jin", "start": 265, "end": 420}, | |
{"lane": 0, "id": "Sui", "start": 580, "end": 615}, | |
{"lane": 0, "id": "Tang", "start": 620, "end": 900}, | |
{"lane": 0, "id": "Song", "start": 960, "end": 1265}, | |
{"lane": 0, "id": "Yuan", "start": 1270, "end": 1365}, | |
{"lane": 0, "id": "Ming", "start": 1370, "end": 1640}, | |
{"lane": 0, "id": "Qing", "start": 1645, "end": 1910}, | |
{"lane": 1, "id": "Yamato", "start": 300, "end": 530}, | |
{"lane": 1, "id": "Asuka", "start": 550, "end": 700}, | |
{"lane": 1, "id": "Nara", "start": 710, "end": 790}, | |
{"lane": 1, "id": "Heian", "start": 800, "end": 1180}, | |
{"lane": 1, "id": "Kamakura", "start": 1190, "end": 1330}, | |
{"lane": 1, "id": "Muromachi", "start": 1340, "end": 1560}, | |
{"lane": 1, "id": "Edo", "start": 1610, "end": 1860}, | |
{"lane": 1, "id": "Meiji", "start": 1870, "end": 1900}, | |
{"lane": 1, "id": "Taisho", "start": 1910, "end": 1920}, | |
{"lane": 1, "id": "Showa", "start": 1925, "end": 1985}, | |
{"lane": 1, "id": "Heisei", "start": 1990, "end": 1995}, | |
{"lane": 2, "id": "Three Kingdoms", "start": 10, "end": 670}, | |
{"lane": 2, "id": "North and South States", "start": 690, "end": 900}, | |
{"lane": 2, "id": "Goryeo", "start": 920, "end": 1380}, | |
{"lane": 2, "id": "Joseon", "start": 1390, "end": 1890}, | |
{"lane": 2, "id": "Korean Empire", "start": 1900, "end": 1945}] | |
items = items.map(v => { | |
v.start = new Date(`${v.start < 10 ? "0" : ""}${v.start < 100 ? "00" : ""}${v.start}-01-01`) | |
v.end = new Date(`${v.end}-12-31`) | |
return v | |
}) | |
timeBegin = new Date("0000") | |
timeEnd = new Date("2000") | |
</script> | |
<script type="text/javascript"> | |
var m = [20, 15, 15, 120] //top right bottom left | |
var w = 960 - m[1] - m[3] | |
var h = 500 - m[0] - m[2] | |
var f = 20 | |
var miniHeight = laneLength * 12 + 50 | |
var mainHeight = h - miniHeight - 50 | |
//scales | |
var x = d3.time.scale() | |
.domain([timeBegin, timeEnd]) | |
.range([0, w]) | |
var x1 = d3.time.scale() | |
.domain([timeBegin, timeEnd]) | |
.range([0, w]) | |
var y1 = d3.scale.linear() | |
.domain([0, laneLength]) | |
.range([0, mainHeight]) | |
var y2 = d3.scale.linear() | |
.domain([0, laneLength]) | |
.range([0, miniHeight]) | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom") | |
.ticks(d3.time.years, 100) | |
.tickFormat(d3.time.format("%Y")) | |
.tickSize(5) | |
.tickPadding(1) | |
var x1Axis = d3.svg.axis() | |
.scale(x1) | |
.orient("bottom") | |
.ticks(d3.time.years, 100) | |
.tickFormat(d3.time.format("%Y")) | |
.tickSize(5) | |
.tickPadding(1) | |
var chart = d3.select("body") | |
.append("svg") | |
.attr("width", w + m[1] + m[3]) | |
.attr("height", h + m[0] + m[2]) | |
.attr("class", "chart") | |
chart.append("defs").append("clipPath") | |
.attr("id", "clip") | |
.append("rect") | |
.attr("width", w) | |
.attr("height", mainHeight) | |
var main = chart.append("g") | |
.attr("transform", `translate(${m[3]}, ${m[0]})`) | |
.attr("width", w) | |
.attr("height", mainHeight) | |
.attr("class", "main") | |
var mini = chart.append("g") | |
.attr("transform", `translate(${m[3]}, ${mainHeight + m[0]})`) | |
.attr("width", w) | |
.attr("height", miniHeight) | |
.attr("class", "mini") | |
var focus = main.append("g") | |
.attr("class", "focus") | |
focus.append("g") | |
.attr("class", "x1 axis") | |
.call(x1Axis) | |
//main lanes and texts | |
main.append("g").selectAll(".laneLines") | |
.data(items) | |
.enter().append("line") | |
.attr("x1", m[1]) | |
.attr("y1", d => y1(d.lane) + f) | |
.attr("x2", w) | |
.attr("y2", d => y1(d.lane) + f) | |
.attr("stroke", "lightgray") | |
main.append("g").selectAll(".laneText") | |
.data(lanes) | |
.enter().append("text") | |
.text(d => d) | |
.attr("x", -m[1]) | |
.attr("y", (d, i) => y1(i + .5) + f) | |
.attr("dy", ".5ex") | |
.attr("text-anchor", "end") | |
.attr("class", "laneText") | |
//mini lanes and texts | |
mini.append("g").selectAll(".laneLines") | |
.data(items) | |
.enter().append("line") | |
.attr("x1", m[1]) | |
.attr("y1", d => y2(d.lane) + f) | |
.attr("x2", w) | |
.attr("y2", d => y2(d.lane) + f) | |
.attr("stroke", "lightgray") | |
mini.append("g").selectAll(".laneText") | |
.data(lanes) | |
.enter().append("text") | |
.text(d => d) | |
.attr("x", -m[1]) | |
.attr("y", (d, i) => y2(i + .5) + f) | |
.attr("dy", ".5ex") | |
.attr("text-anchor", "end") | |
.attr("class", "laneText") | |
var itemRects = main.append("g") | |
.attr("clip-path", "url(#clip)") | |
//mini item rects | |
mini.append("g").selectAll("miniItems") | |
.data(items) | |
.enter().append("rect") | |
.attr("class", d => `miniItem${d.lane}`) | |
.attr("x", d => x(d.start)) | |
.attr("y", d => y2(d.lane + .5) - 5 + f) | |
.attr("width", d => x(d.end) - x(d.start)) | |
.attr("height", 10) | |
//mini labels | |
mini.append("g").selectAll(".miniLabels") | |
.data(items) | |
.enter().append("text") | |
.text(d => d.id) | |
.attr("x", d => x(d.start)) | |
.attr("y", d => y2(d.lane + .5) + f) | |
.attr("dy", ".5ex") | |
//brush | |
var brush = d3.svg.brush() | |
.x(x) | |
.on("brush", display) | |
mini.append("g") | |
.attr("class", "x brush") | |
.call(brush) | |
.selectAll("rect") | |
.attr("y", 1 + f) | |
.attr("height", miniHeight - 1) | |
display() | |
function display() { | |
var rects | |
var labels | |
var minExtent = brush.empty() ? x1.domain()[0] : brush.extent()[0] | |
var maxExtent = brush.empty() ? x1.domain()[1] : brush.extent()[1] | |
var visItems = items.filter(d => d.start < maxExtent && d.end > minExtent) | |
if (!brush.empty()) { | |
mini.select(".brush") | |
.call(brush.extent([minExtent, maxExtent])) | |
} | |
x1.domain([minExtent, maxExtent]) | |
focus.select(".x1.axis").call(x1Axis) | |
//update main item rects | |
rects = itemRects.selectAll("rect") | |
.data(visItems, d => d.id) | |
.attr("x", d => x1(d.start)) | |
.attr("width", d => x1(d.end) - x1(d.start)) | |
rects.enter().append("rect") | |
.attr("class", d => "miniItem" + d.lane) | |
.attr("x", d => x1(d.start)) | |
.attr("y", d => y1(d.lane) + 10 + f) | |
.attr("width", d => x1(d.end) - x1(d.start)) | |
.attr("height", d => .8 * y1(1)) | |
rects.exit().remove() | |
//update the item labels | |
labels = itemRects.selectAll("text") | |
.data(visItems, d => d.id) | |
.attr("x", d => x1(Math.max(d.start, minExtent) + 2)) | |
labels.enter().append("text") | |
.text(d => d.id) | |
.attr("x", d => x1(Math.max(d.start, minExtent))) | |
.attr("y", d => y1(d.lane + .5)) | |
.attr("text-anchor", "start") | |
labels.exit().remove() | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment