|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> /* set the CSS */ |
|
|
|
.line { |
|
fill: none; |
|
stroke: steelblue; |
|
stroke-width: 2px; |
|
} |
|
|
|
.area { |
|
fill: lightsteelblue; |
|
} |
|
|
|
.sla__line { |
|
stroke: #F74C7D; |
|
stroke-width: 2; |
|
stroke-dasharray: 6 3; |
|
} |
|
|
|
.sla__label { |
|
fill: white; |
|
} |
|
|
|
.issue__line { |
|
stroke: steelblue; |
|
stroke-width: 20; |
|
opacity: .5; |
|
} |
|
|
|
</style> |
|
<body> |
|
|
|
<!-- load the d3.js library --> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
// set the dimensions and margins of the graph |
|
const margin = {top: 20, right: 20, bottom: 30, left: 80}; |
|
const width = 960 - margin.left - margin.right; |
|
const height = 500 - margin.top - margin.bottom; |
|
const arr = []; |
|
|
|
// parse the date / time |
|
const dateFormat = "%B %d, %Y"; |
|
const parseTime = d3.timeParse(dateFormat); |
|
const formatTime = d3.timeFormat(dateFormat); |
|
const randNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; |
|
|
|
// Set up random data |
|
const numDays = 45; |
|
const graphData = Array(numDays) |
|
.fill() |
|
.map((_, index) => { |
|
const newD = new Date(); |
|
newD.setDate(newD.getDate() - index); |
|
return { |
|
date: parseTime(formatTime(newD)), |
|
value: randNum(5, 15) |
|
} |
|
}); |
|
|
|
const fillArr = (num) => { |
|
if (arr.includes(num)) { |
|
fillArr(randNum(num - 1, 20)); |
|
} else { |
|
arr.push(num); |
|
return num; |
|
} |
|
}; |
|
|
|
for (let i=0; i < 11; i++) { |
|
fillArr(randNum(1, 20)); |
|
} |
|
|
|
const lineData = Array(10) |
|
.fill() |
|
.map((_, index) => graphData[arr[index]]); |
|
|
|
// set the ranges |
|
const x = d3.scaleTime().range([0, width]); |
|
const y = d3.scaleLinear().range([height, 0]); |
|
|
|
// define the area |
|
const area = d3.area() |
|
.x(d => x(d.date)) |
|
.y0(height) |
|
.y1(d => y(d.value)) |
|
.curve(d3.curveMonotoneX); |
|
|
|
// define the line |
|
const valueline = d3.line() |
|
.x(d => x(d.date)) |
|
.y(d => y(d.value)) |
|
.curve(d3.curveMonotoneX); |
|
|
|
// append the svg obgect to the body of the page |
|
const svg = d3.select("body") |
|
.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 + ")"); |
|
|
|
// scale the range of the data |
|
x.domain(d3.extent(graphData, d => d.date)); |
|
y.domain([0, d3.max(graphData, d => d.value + 5)]); |
|
|
|
// add the X Axis |
|
svg.append("g") |
|
.attr("class", "x-axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x)); |
|
|
|
// add the Y Axis |
|
svg.append("g") |
|
.attr("class", "y-axis") |
|
.call(d3.axisLeft(y)); |
|
|
|
// 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) |
|
|
|
// add the area |
|
svg.append("path") |
|
.data([graphData]) |
|
.attr("class", "area") |
|
.attr("d", area); |
|
|
|
// add the valueline path. |
|
svg.append("path") |
|
.data([graphData]) |
|
.attr("class", "line") |
|
.attr("d", valueline); |
|
|
|
// Draw HZ line |
|
addLine(svg); |
|
|
|
// Draw Vert lines |
|
addVertLines(svg); |
|
|
|
function addVertLines(svg) { |
|
const vertLine = svg.selectAll('.issue') |
|
.data(lineData) |
|
.enter() |
|
.append('g') |
|
.attr('transform', d => `translate(${x(d.date)}, 0)`); |
|
|
|
vertLine.append('line') |
|
.attr('y1', 0) |
|
.attr('y2', height) |
|
.attr('class', 'issue__line'); |
|
} |
|
|
|
function addVertLine(pos) { |
|
svg.append("line") |
|
.attr("x1", x(today)) //<<== change your code here |
|
.attr("y1", 0) |
|
.attr("x2", x(today)) //<<== and here |
|
.attr("y2", height - margin.top - margin.bottom) |
|
.style("stroke-width", 2) |
|
.style("stroke", "red") |
|
.style("fill", "none"); |
|
} |
|
|
|
function addLine(svg) { |
|
const rectHeight = 20; |
|
const rectYoffset = -10; |
|
const rectXoffset = 0; |
|
const rectPadding = 10; |
|
|
|
// Add SLA element group and position |
|
const sla = svg.selectAll('.sla') |
|
.data([10]) |
|
.enter() |
|
.append('g') |
|
.attr('transform', d => `translate(-50, ${y(d)})`); |
|
|
|
// Add SLA line |
|
sla.append('line') |
|
.attr('x1', 0) |
|
.attr('x2', width + 50) |
|
.attr('class', 'sla__line'); |
|
|
|
// Add SLA label group (default position) |
|
const labelEl = sla.append('g'); |
|
|
|
// Position text box |
|
const rectEl = labelEl.append('rect') |
|
.attr('rx', '3') |
|
.attr('ry', '3') |
|
.attr('fill', '#F74C7D') |
|
.attr('height', rectHeight) |
|
.attr('transform', `translate(${rectXoffset}, ${-rectHeight - rectYoffset})`); |
|
|
|
// Position text |
|
const textEl = labelEl.append('text') |
|
.attr('class', 'sla__label') |
|
.text(d => `${d}ms`) |
|
.attr('dx', rectPadding / 2) |
|
.attr('dy', rectPadding / 2); |
|
|
|
// Read box width from text length |
|
rectEl |
|
.attr('width', textEl.node().getComputedTextLength() + rectPadding); |
|
} |
|
</script> |
|
</body> |