<!doctype html>
<html>
<meta charset="utf-8" />
<title>D3 chart</title>
<style>
body {
margin: 1em auto 4em auto;
position: relative;
width: 960px;
}
svg {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis line {
shape-rendering: auto;
}
.line {
fill: none;
stroke: #000;
stroke-width: 0.8px;
}
.bar {
fill: steelblue;
opacity: 0.8;
}
.area {
fill: steelblue;
opacity: 0.8;
}
.grid-line {
stroke: lightgray;
stroke-width: 0.5;
}
#container {
display: flex;
gap: 10px;
padding: 10px;
}
#container > div {
border: 1px solid #ccc;
padding: 10px;
box-sizing: border-box;
}
#container > div:first-child {
flex: 1;
min-width: 200px;
}
#container > div:last-child {
flex: 3;
}
#chart-container {
display: flex;
flex-direction: column;
}
div > p {
border: 1px solid #ccc;
padding: 10px;
box-sizing: border-box;
}
div > pre {
border: 1px solid #ccc;
padding: 10px;
box-sizing: border-box;
}
</style>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
const n = 60
const duration = 750
const random = d3.random.normal(0, 0.2)
const margin = { top: 6, right: 6, bottom: 20, left: 25 }
const width = 720 - margin.right
const height = 120 - margin.top - margin.bottom
const $ = (id) => document.getElementById(id)
// --- Create SVG --------------------------
function createSvg(data, id, xDomain) {
var x = d3.scale.linear().domain(xDomain).range([0, width])
var now = new Date(Date.now() - duration)
var lastIndex = xDomain[1]
var timeseries = d3.time
.scale()
.domain([now - lastIndex * duration, now - duration])
.range([0, width])
var y = d3.scale
.linear()
.domain([d3.min(data), d3.max(data)])
.range([height, 0])
var baseSvg = d3
.select(id)
.append('p')
.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 + ')')
baseSvg //
.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width)
.attr('height', height)
var axisY = baseSvg
.append('g')
.attr('class', 'y axis')
.call(
(y.axis = d3.svg //
.axis()
.scale(y)
.ticks(5)
.orient('left')),
)
var axisX = baseSvg
.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(
(timeseries.axis = d3.svg //
.axis()
.scale(timeseries)
.orient('bottom')),
)
return { lastIndex, x, timeseries, axisX, y, axisY, baseSvg }
}
// --- Update Axis --------------------------
function updateAxis(data, svg) {
const { timeseries, lastIndex, axisX, y, axisY } = svg
const now = new Date()
timeseries.domain([now - lastIndex * duration, now - duration])
axisX.call(timeseries.axis)
y.domain([d3.min(data), d3.max(data)])
axisY.call(y.axis)
const ticks = axisY.selectAll('g.tick')
ticks.each(function () {
const tick = d3.select(this)
tick.selectAll('.grid-line').remove()
tick //
.append('line')
.attr('class', 'grid-line')
.attr('x1', 0)
.attr('x2', width)
.attr('stroke-dasharray', '3,6')
})
}
// --- Line Chart --------------------------
function lineChart(id, xDomain, interpolation, tick) {
var data = d3.range(n).map(() => 0)
var svg = createSvg(data, id, xDomain)
var line = d3.svg
.line()
.interpolate(interpolation)
.x((d, i) => svg.x(i))
.y((d) => svg.y(d))
var path = svg.baseSvg //
.append('g')
.attr('clip-path', 'url(#clip)')
.append('path')
.datum(data)
.attr('class', 'line')
.attr('d', line)
tick(path, line, data, svg)
}
// --- Bar Chart --------------------------
function barChart(id, xDomain, tick) {
var data = d3.range(n).map(() => 0)
var svg = createSvg(data, id, xDomain)
var bars = svg.baseSvg //
.append('g')
.attr('class', 'bars')
.attr('clip-path', 'url(#clip)')
.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => svg.x(i))
.attr('y', (d) => svg.y(d))
.attr('width', width / n - 1)
.attr('height', (d) => height - svg.y(d))
tick(bars, data, svg)
}
// --- Area Chart --------------------------
function areaChart(id, xDomain, interpolation, tick) {
var data = d3.range(n).map(() => 0)
var svg = createSvg(data, id, xDomain)
var area = d3.svg
.area()
.interpolate(interpolation)
.x((d, i) => svg.x(i))
.y0(svg.y(0))
.y1((d) => svg.y(d))
var path = svg.baseSvg //
.append('g')
.attr('clip-path', 'url(#clip)')
.append('path')
.datum(data)
.attr('class', 'area')
.attr('d', area)
tick(path, area, data, svg)
}
// --- Chart1 --------------------------
function Chart1() {
var transition = d3.select({}).transition().duration(duration).ease('linear')
function tick(path, line, data, svg) {
function each() {
data.push(random())
showData('chart1-data', data)
updateAxis(data, svg)
path
.attr('d', line)
.attr('transform', null)
.transition()
.attr('transform', 'translate(' + svg.x(-1) + ')')
data.shift()
}
transition = transition
.each(each)
.transition()
.each('start', () => tick(path, line, data, svg))
}
lineChart('#chart1', [0, n - 1], 'linear', tick)
}
// --- Chart2 --------------------------
function Chart2() {
var transition = d3.select({}).transition().duration(duration).ease('linear')
function tick(path, line, data, svg) {
function each() {
data.push(random())
showData('chart2-data', data)
updateAxis(data, svg)
path
.attr('d', line)
.attr('transform', null)
.transition()
.attr('transform', 'translate(' + svg.x(0) + ')')
data.shift()
}
transition = transition
.each(each)
.transition()
.each('start', () => tick(path, line, data, svg))
}
lineChart('#chart2', [1, n - 2], 'basis', tick)
}
// --- Chart3 --------------------------
function Chart3() {
var transition = d3.select({}).transition().duration(duration).ease('linear')
function tick(bars, data, svg) {
function each() {
data.push(random())
showData('chart3-data', data)
updateAxis(data, svg)
svg.x.domain([svg.x.domain()[0] + 1, svg.x.domain()[1] + 1])
bars = bars.data(data)
bars
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => svg.x(i))
.attr('y', (d) => svg.y(d))
.attr('width', width / n - 1)
.attr('height', (d) => height - svg.y(d))
.attr('fill', 'steelblue')
bars
.transition()
.duration(duration)
.attr('x', (d, i) => svg.x(i))
}
transition = transition
.each(each)
.transition()
.each('start', () => tick(bars, data, svg))
}
barChart('#chart3', [0, n - 1], tick)
}
// --- Chart4 --------------------------
function Chart4() {
var transition = d3.select({}).transition().duration(duration).ease('linear')
function tick(path, area, data, svg) {
function each() {
data.push(random())
showData('chart4-data', data)
updateAxis(data, svg)
path
.attr('d', area)
.attr('transform', null)
.transition()
.attr('transform', 'translate(' + svg.x(-1) + ')')
data.shift()
}
transition = transition
.each(each)
.transition()
.each('start', () => tick(path, area, data, svg))
}
areaChart('#chart4', [0, n - 1], 'linear', tick)
}
// --- Chart5 --------------------------
function Chart5() {
var transition = d3.select({}).transition().duration(duration).ease('linear')
function tick(path, area, data, svg) {
function each() {
data.push(random())
showData('chart5-data', data)
updateAxis(data, svg)
path
.attr('d', area)
.attr('transform', null)
.transition()
.attr('transform', 'translate(' + svg.x(-1) + ')')
data.shift()
}
transition = transition
.each(each)
.transition()
.each('start', () => tick(path, area, data, svg))
}
areaChart('#chart5', [0, n - 1], 'basis', tick)
}
function showData(id, data) {
$(id).innerText = JSON.stringify(data.slice(-8), null, 2)
}
</script>
<h1>D3 Real-time Chart</h1>
<div id="container">
<div>
Chart1 Data
<pre id="chart1-data"></pre>
Chart2 Data
<pre id="chart2-data"></pre>
Chart3 Data
<pre id="chart3-data"></pre>
Chart4 Data
<pre id="chart4-data"></pre>
Chart5 Data
<pre id="chart5-data"></pre>
</div>
<div id="chart-container">
<div id="chart1">
Chart1 Line linear
<script>
Chart1()
</script>
</div>
<div id="chart2">
Chart2 Line basis
<script>
Chart2()
</script>
</div>
<div id="chart3">
Chart3 Bar
<script>
Chart3()
</script>
</div>
<div id="chart4">
Chart4 Area linear
<script>
Chart4()
</script>
</div>
<div id="chart5">
Chart5 Area basis
<script>
Chart5()
</script>
</div>
</div>
</div>
</html>
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme