Built with blockbuilder.org
forked from areologist's block: axes and margins
forked from areologist's block: axes and margins - responsive
forked from areologist's block: column chart
forked from areologist's block: scatter plot
license: mit |
Built with blockbuilder.org
forked from areologist's block: axes and margins
forked from areologist's block: axes and margins - responsive
forked from areologist's block: column chart
forked from areologist's block: scatter plot
[ | |
{ "timing": 250, "count": 1, "date": 1523163600000 }, | |
{ "timing": 450, "count": 10, "date": 1523163600000 }, | |
{ "timing": 750, "count": 2, "date": 1523163600000 }, | |
{ "timing": 100, "count": 1, "date": 1523250000000 }, | |
{ "timing": 290, "count": 7, "date": 1523250000000 }, | |
{ "timing": 490, "count": 2, "date": 1523250000000 }, | |
{ "timing": 450, "count": 80, "date": 1523336400000 }, | |
{ "timing": 600, "count": 2, "date": 1523336400000 }, | |
{ "timing": 250, "count": 3, "date": 1523422800000 }, | |
{ "timing": 450, "count": 1, "date": 1523422800000 }, | |
{ "timing": 250, "count": 3, "date": 1523509200000 }, | |
{ "timing": 450, "count": 10, "date": 1523509200000 }, | |
{ "timing": 750, "count": 2, "date": 1523509200000 }, | |
{ "timing": 250, "count": 3, "date": 1523595600000 }, | |
{ "timing": 450, "count": 10, "date": 1523595600000 }, | |
{ "timing": 750, "count": 2, "date": 1523595600000 }, | |
{ "timing": 100, "count": 65, "date": 1523682000000 }, | |
{ "timing": 120, "count": 1, "date": 1523768400000 }, | |
{ "timing": 450, "count": 20, "date": 1523768400000 }, | |
{ "timing": 750, "count": 80, "date": 1523768400000 }, | |
{ "timing": 550, "count": 3, "date": 1523854800000 }, | |
{ "timing": 700, "count": 25, "date": 1523854800000 }, | |
{ "timing": 800, "count": 1, "date": 1523854800000 }, | |
{ "timing": 300, "count": 3, "date": 1523941200000 }, | |
{ "timing": 350, "count": 46, "date": 1523941200000 }, | |
{ "timing": 475, "count": 2, "date": 1523941200000 } | |
] |
[ | |
{ "timing": 250, "count": 1, "date": 0 }, | |
{ "timing": 450, "count": 10, "date": 0 }, | |
{ "timing": 750, "count": 2, "date": 0 }, | |
{ "timing": 100, "count": 1, "date": 1 }, | |
{ "timing": 290, "count": 7, "date": 1 }, | |
{ "timing": 490, "count": 2, "date": 1 }, | |
{ "timing": 450, "count": 40, "date": 2 }, | |
{ "timing": 600, "count": 2, "date": 2 }, | |
{ "timing": 250, "count": 3, "date": 3 }, | |
{ "timing": 450, "count": 1, "date": 3 }, | |
{ "timing": 250, "count": 3, "date": 4 }, | |
{ "timing": 450, "count": 10, "date": 4 }, | |
{ "timing": 750, "count": 2, "date": 4 }, | |
{ "timing": 250, "count": 3, "date": 5 }, | |
{ "timing": 450, "count": 10, "date": 5 }, | |
{ "timing": 750, "count": 2, "date": 5 }, | |
{ "timing": 250, "count": 3, "date": 6 }, | |
{ "timing": 450, "count": 10, "date": 6 }, | |
{ "timing": 250, "count": 3, "date": 7 }, | |
{ "timing": 450, "count": 10, "date": 7 }, | |
{ "timing": 750, "count": 2, "date": 7 }, | |
{ "timing": 250, "count": 3, "date": 8 }, | |
{ "timing": 450, "count": 10, "date": 8 }, | |
{ "timing": 750, "count": 2, "date": 8 }, | |
{ "timing": 250, "count": 3, "date": 9 }, | |
{ "timing": 450, "count": 10, "date": 9 }, | |
{ "timing": 750, "count": 2, "date": 9 } | |
] |
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.js"></script> | |
<style> | |
.chart { | |
background-color: #fcfcfc; | |
} | |
.domain { | |
stroke: #E3E3E3; | |
} | |
.tick text, | |
.sla-label { | |
font-size: 14px; | |
font-family: monospace; | |
fill: #848E99; | |
} | |
.sla-label { | |
fill: #fff; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="chart"></div> | |
<script> | |
const margin = { top: 40, right: 35, bottom: 80, left: 80 }; | |
const width = 900 - margin.left - margin.right; | |
const height = 400 - margin.top - margin.bottom; | |
const fullWidth = width + margin.left + margin.right; | |
const fullHeight = height + margin.top + margin.bottom; | |
const svg = d3.select('.chart') | |
.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 + ')'); | |
d3.json('data-timestamps.json', (err, data) => { | |
// DEFINE SCALES | |
const xScale = d3.scaleTime() | |
.domain(d3.extent(data, d => d.date)) | |
.range([0, width]) | |
.nice(); | |
const yScale = d3.scaleLinear() | |
.domain([0, d3.max(data, d => d.timing)]) | |
.range([height, 0]) | |
.nice(); | |
const rScale = d3.scaleSqrt() | |
.domain([0, d3.max(data, d => d.count)]) | |
.range([2, 20]); | |
// DEFINE AXES | |
const yAxis = d3.axisLeft(yScale) | |
.tickFormat(v => `${v}ms`); | |
const xAxis = d3.axisBottom(xScale) | |
.tickFormat((v) => dateFns.format(v, 'MMM D')); | |
// ADD AXES | |
svg.append('g') | |
.attr('class', 'y-axis') | |
.call(yAxis); | |
svg.append('g') | |
.attr('class', 'x-axis') | |
.attr('transform', `translate(0, ${height})`) | |
.call(xAxis); | |
// ADD GRID | |
svg.selectAll('.y-axis .tick line') | |
.attr('stroke', '#E3E3E3') | |
.attr('stroke-width', 1) | |
.attr('x1', 0) | |
.attr('x2', width); | |
svg.selectAll('.x-axis .tick line') | |
.attr('stroke', '#E3E3E3') | |
.attr('stroke-width', 1) | |
.attr('y1', 0) | |
.attr('y2', -height) | |
// DEFINE BUBBLES | |
const bubbles = svg.selectAll('.bubble') | |
.data(data) | |
.enter() | |
.append('g') | |
.attr('class', 'bubble') | |
.attr('transform', d => { | |
return `translate(${xScale(d.date)}, ${yScale(d.timing)})` | |
}) | |
.attr('opacity', 0.8) | |
// DEFINE SLA Line to indicate long request times | |
const sla = svg.selectAll('.sla') | |
.data([500]) | |
.enter() | |
.append('g') | |
.attr('class', 'sla') | |
.attr('transform', d => | |
`translate(0, ${yScale(d)})` | |
); | |
// ADD SLA LINE | |
sla.append('line') | |
.attr('x1', 0) | |
.attr('x2', width) | |
.attr('stroke-width', 2) | |
.attr('stroke-dasharray', '6 3') | |
.attr('stroke', '#F74C7D') | |
// ADD SLA LABELS | |
const rectHeight = 20; | |
const rectOffset = 2; | |
const rectPadding = 10; | |
sla.append('rect') | |
.attr('height', rectHeight) | |
.attr('fill', '#F74C7D') | |
.attr('rx', '3') | |
.attr('ry', '3'); | |
sla.append('text') | |
.attr('class', 'sla-label') | |
.text(d => `${d}ms`); | |
// POSITION SLA RECT | |
sla.selectAll('rect') | |
.attr('width', function() { | |
return this.parentNode.querySelector('.sla-label') | |
.getComputedTextLength() + rectPadding; | |
}) | |
.attr('transform', `translate(0, ${-rectHeight - rectOffset})`); | |
// POSTION SLA TEXT | |
sla.selectAll('text') | |
.attr('dx', rectPadding / 2) | |
.attr('dy', function() { | |
const rectBox = this.parentNode.querySelector('rect').getBoundingClientRect(); | |
const textBox = this.getBoundingClientRect(); | |
return -((rectBox.height / 2) - (textBox.height / 3) + rectOffset); | |
}) | |
// ADD BUBBLES | |
bubbles.append('circle') | |
.attr('cx', 0) | |
.attr('cy', 0) | |
.attr('r', d => rScale(d.count)) | |
.attr('fill', '#4F65FF'); | |
}); | |
</script> | |
</body> |