require:
- D3.js v4
- coffeescript
by TBD | @tbdr
forked from TBD's block: timeline chart with zoom
license: mit |
require:
by TBD | @tbdr
forked from TBD's block: timeline chart with zoom
(function() { | |
var i, j; | |
window.VEHICLES = []; | |
for (i = j = 1; j <= 15; i = ++j) { | |
VEHICLES.push({ | |
tir: i, | |
start: new Date(+(new Date()) - Math.floor(Math.random() * 1000000000)), | |
end: new Date() | |
}); | |
} | |
}).call(this); |
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="http://coffeescript.org/v1/browser-compiler/coffee-script.js"></script> | |
<style> | |
.chart { | |
margin: 0 auto; | |
width: 960px; | |
min-height: 8px; | |
height: 100%; | |
box-shadow: 0 0 3px 1px #eee; | |
} | |
svg { | |
vertical-align: top; | |
width: 100%; | |
height: 100%; | |
} | |
.tooltip { | |
position: absolute; | |
} | |
.axis { | |
height: 20px; | |
} | |
.zoom { | |
cursor: move; | |
position: absolute; | |
top: 0; | |
left: 0; | |
fill: none; | |
pointer-events: all; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="chart"></div> | |
<script src="data.js"></script> | |
<script type="text/coffeescript"> | |
wrapper = document.querySelector '.chart' | |
width = wrapper.clientWidth; | |
parser = d3.isoParse | |
minX = d3.min(VEHICLES, (d) -> parser d.start) | |
maxX = d3.max(VEHICLES, (d) -> parser d.end) | |
# --- X scale | |
x = d3.scaleTime() | |
.domain([minX, maxX]) | |
.rangeRound([0, width]) | |
# --- nest all the data | |
symbols = d3.nest() | |
.key (d) -> d.tir | |
.entries(VEHICLES) | |
height = wrapper.getBoundingClientRect().bottom | |
spanX = (d) -> x(parser(d.start)) | |
spanW = (d) -> x(parser(d.end)) - x(parser(d.start)) | |
chart = (symbol) -> | |
svg = d3.select this | |
svg.selectAll 'rect' | |
.data(symbol.values) | |
.enter() | |
.append 'rect' | |
.attr 'x', (d) -> spanX d | |
.attr 'y', 0 | |
.attr 'width', (d) -> spanW d | |
.attr 'height', height | |
.attr 'fill', (d) -> d.color or '#ddf' | |
.on 'mouseover', (d) -> | |
tooltip.html [d.tir, d.start, d.end].join('<br>') | |
.on 'mouseout', () -> | |
tooltip.html '' | |
# --- add all charts | |
allCharts = d3.select(wrapper).selectAll('svg') | |
.data(symbols) | |
.enter() | |
.append('svg') | |
.attr('height', height) | |
.each(chart) | |
# --- add X axis | |
xAxis = d3.axisBottom(x) | |
.ticks(width / 100) | |
globalX = d3.select(wrapper) | |
.append('svg') | |
.attr('class', 'axis') | |
.call(xAxis) | |
# --- catch all zoom svg rect | |
catchAll = d3.select('body') | |
.append 'svg' | |
.attr 'class', 'zoom' | |
.append 'rect' | |
.attr 'fill', 'none' | |
.attr 'width', width | |
.attr 'height', wrapper.getBoundingClientRect().bottom | |
# --- add tooltip | |
tooltip = d3.select(wrapper).append('div') | |
.attr 'class', 'tooltip' | |
catchAll.call(d3.zoom() | |
.scaleExtent([0.1, 10]) | |
.on "zoom", -> | |
transform = d3.event.transform | |
globalX.call xAxis.scale(transform.rescaleX(x)) | |
allCharts.selectAll('rect') | |
.attr 'x', (d) -> transform.applyX spanX d | |
.attr 'width', (d) -> transform.k * spanW d | |
) | |
</script> | |
</body> |