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> |