weixinxiaochengxu/utils/chart.js

120 lines
3.1 KiB
JavaScript

var PADDING = { top: 24, right: 16, bottom: 44, left: 48 }
function drawChart(ctx, w, h, opts) {
var xLabels = opts.xLabels
var series = opts.series
var colors = opts.colors
if (!xLabels.length || !series.length) return
ctx.clearRect(0, 0, w, h)
var plotW = w - PADDING.left - PADDING.right
var plotH = h - PADDING.top - PADDING.bottom
var cx = PADDING.left
var cy = PADDING.top
var yMin = Infinity, yMax = -Infinity
series.forEach(function (s) {
s.data.forEach(function (v) {
if (v !== null && v !== undefined) {
if (v < yMin) yMin = v
if (v > yMax) yMax = v
}
})
})
if (!isFinite(yMin) || !isFinite(yMax)) return
var yRange = yMax - yMin || 1
yMin = Math.floor(yMin - yRange * 0.1)
yMax = Math.ceil(yMax + yRange * 0.1)
if (yMin < 0 && yMin + yRange * 0.1 > 0) yMin = 0
var realRange = yMax - yMin
var toX = function (i) { return cx + (i / Math.max(xLabels.length - 1, 1)) * plotW }
var toY = function (v) { return cy + plotH - ((v - yMin) / realRange) * plotH }
var gridCount = Math.min(5, Math.ceil(realRange))
ctx.strokeStyle = "#edf2f7"
ctx.lineWidth = 0.5
ctx.setLineDash([4, 4])
ctx.font = "10px -apple-system, sans-serif"
ctx.fillStyle = "#a0aec0"
ctx.textAlign = "right"
ctx.textBaseline = "middle"
for (var i = 0; i <= gridCount; i++) {
var val = yMin + (realRange * i) / gridCount
var y = toY(val)
ctx.beginPath()
ctx.moveTo(cx, y)
ctx.lineTo(cx + plotW, y)
ctx.stroke()
ctx.fillText(formatVal(val), cx - 6, y)
}
ctx.setLineDash([])
var maxLabels = Math.floor(plotW / 50)
var step = Math.max(1, Math.ceil(xLabels.length / maxLabels))
ctx.textAlign = "center"
ctx.textBaseline = "top"
ctx.fillStyle = "#718096"
for (var i = 0; i < xLabels.length; i += step) {
ctx.fillText(xLabels[i], toX(i), cy + plotH + 8)
}
ctx.strokeStyle = "#cbd5e0"
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(cx, cy)
ctx.lineTo(cx, cy + plotH)
ctx.lineTo(cx + plotW, cy + plotH)
ctx.stroke()
series.forEach(function (s, si) {
var color = colors[si % colors.length]
var points = s.data
.map(function (v, i) {
if (v !== null && v !== undefined) {
return { x: toX(i), y: toY(v), v: v }
}
return null
})
.filter(function (p) { return p !== null })
if (points.length < 2) return
ctx.strokeStyle = color
ctx.lineWidth = 2
ctx.lineJoin = "round"
ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y)
}
ctx.stroke()
points.forEach(function (p) {
ctx.fillStyle = "#fff"
ctx.beginPath()
ctx.arc(p.x, p.y, 3.5, 0, Math.PI * 2)
ctx.fill()
ctx.fillStyle = color
ctx.beginPath()
ctx.arc(p.x, p.y, 2.5, 0, Math.PI * 2)
ctx.fill()
})
})
ctx.draw()
}
function formatVal(v) {
if (v === 0) return "0"
var abs = Math.abs(v)
if (abs >= 1000) return (v / 1000).toFixed(1).replace(/\.0$/, "") + "k"
if (abs >= 1) return Number(v.toFixed(1)).toString()
return Number(v.toFixed(2)).toString()
}
module.exports = { drawChart: drawChart }