下面的代码本人亲自撰写,原生不易啊。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="myCanvas" width="400" height="300"></canvas>
</body>
<script>
class MyCanvas {
static dom = null;
static ctx = null;
static canvasWidth = 0;
static canvasHeight = 0;
static config = null;
static valx = 0;
static valy = 0;
static xlist = [];
static ylist = [];
static lenTrue = 0;
init(dom) {
MyCanvas.dom = dom
MyCanvas.ctx = MyCanvas.dom.getContext('2d');
MyCanvas.canvasWidth = MyCanvas.ctx.canvas.width;
MyCanvas.canvasHeight = MyCanvas.ctx.canvas.height;
}
// 配置参数
setOption(config) {
MyCanvas.ctx.clearRect(0, 0, MyCanvas.dom.width, MyCanvas.dom.height);
MyCanvas.config = config
MyCanvas.xlist = config.series.data.map(e => e[0]);
MyCanvas.ylist = config.series.data.map(e => e[1]);
MyCanvas.valx = MyCanvas.x1valjpx()
MyCanvas.valy = MyCanvas.y1valjpx()
MyCanvas.ctx.lineWidth = 1;
MyCanvas.xGraduate()
MyCanvas.xDashedLine()
MyCanvas.yGraduate()
MyCanvas.yDashedLine()
MyCanvas.curve()
}
static getRandomColor() {
let color = `hsl(${Random(0, 360)},${Random(50, 100)}%,${Random(20, 60)}%)`
function Random(min, max) {
return Math.round(Math.random() * (max - min)) + min;
}
return color
}
// 绘制曲线
static curve() {
const left = MyCanvas.config.grid.left
const curvature = MyCanvas.config.series.curvature
// xhb:曲线两个落点都是y轴最大的px值
const xhb = MyCanvas.canvasHeight - 20
MyCanvas.ctx.beginPath();
MyCanvas.ctx.setLineDash([]);
MyCanvas.ylist.forEach((e, i) => {
MyCanvas.ctx.strokeStyle = MyCanvas.getRandomColor();
MyCanvas.ctx.lineWidth = 3;
// xy是控制点的xy坐标
const x = MyCanvas.xlist[i] * MyCanvas.valx + left
const y = e * MyCanvas.valy * 2
const line = new Path2D();
line.name = MyCanvas.config.series.data[i][2]
line.moveTo(x - curvature, xhb);
line.quadraticCurveTo(x, xhb - y, x + curvature, xhb);
MyCanvas.ctx.stroke(line);
MyCanvas.dom.addEventListener("mousemove", (event) => {
const isPointInPath = MyCanvas.ctx.isPointInStroke(line, event.offsetX, event.offsetY);
if (isPointInPath) {
// 曲线被悬浮
console.log(line);
}
});
})
}
// 求x轴每一份值对应了多少实际px值
static x1valjpx() {
const max = Math.max(...MyCanvas.xlist)
const interval = MyCanvas.config.xAxis.interval
const curvature = MyCanvas.config.series.curvature
// 总间隔数
const len = Math.ceil(max / interval)
// 总值数
const valz = len * interval
const left = MyCanvas.config.grid.left
const right = MyCanvas.config.grid.right
const xw = MyCanvas.canvasWidth - left - right
// 求出每一份x值对应的图表里面的x轴px数值
const valx = xw / valz
const maxql = max * valx + curvature
if (maxql > xw) {
// 说明最大x将会超出可画范围,需要增加竖线
let lenTrue = len
let maxqlTrue = maxql
let valxTrue = valx
while (maxqlTrue > xw) {
lenTrue++
// 总值数
const valz = lenTrue * interval
// 求出每一份x值对应的图表里面的y轴px数值
valxTrue = xw / valz
maxqlTrue = max * valxTrue + curvature
}
MyCanvas.lenTrue = lenTrue
return valxTrue
}
return valx
}
// 求y轴每一份值对应了多少实际px值
static y1valjpx() {
const max = Math.max(...MyCanvas.ylist)
const interval = MyCanvas.config.yAxis.interval
const curvature = MyCanvas.config.series.curvature
// 总间隔数
const len = Math.ceil(max / interval)
// 总值数
const valz = len * interval
const top = MyCanvas.config.grid.top
const bottom = MyCanvas.config.grid.bottom
const xh = MyCanvas.canvasHeight - top - bottom
// 求出每一份y值对应的图表里面的y轴px数值
return xh / valz
}
// 绘制x轴刻度值,从左到右画
static xGraduate() {
const grid = MyCanvas.config.grid
const xAxis = MyCanvas.config.xAxis
const gridLen = Object.keys(grid).length
const curvature = MyCanvas.config.series.curvature
MyCanvas.ctx.beginPath();
MyCanvas.ctx.strokeStyle = '#000';
MyCanvas.ctx.textAlign = "center";
MyCanvas.ctx.textBaseline = "top";
MyCanvas.ctx.font = "14px Arial";
if (gridLen) {
const left = MyCanvas.config.grid.left
const right = MyCanvas.config.grid.right
const top = MyCanvas.config.grid.top
const bottom = MyCanvas.config.grid.bottom
const interval = MyCanvas.config.xAxis.interval
let len = 0
if (MyCanvas.lenTrue) {
len = MyCanvas.lenTrue
} else {
const max = Math.max(...MyCanvas.lxList)
len = Math.ceil(max / interval)
}
const xw = MyCanvas.canvasWidth - left - right
const jg = xw / len
for (let i = 0; i < len + 1; i++) {
if (i) {
MyCanvas.ctx.fillText(interval * i, left + (jg * i), MyCanvas.canvasHeight - bottom + xAxis.kdjl)
}
}
}
}
// 绘制x刻度虚线,从右到左画
static xDashedLine() {
const grid = MyCanvas.config.grid
const gridLen = Object.keys(grid).length
const curvature = MyCanvas.config.series.curvature
MyCanvas.ctx.beginPath();
MyCanvas.ctx.strokeStyle = MyCanvas.config.xAxis.dashedColor;
MyCanvas.ctx.setLineDash([3, 2]);
if (gridLen) {
const left = MyCanvas.config.grid.left
const right = MyCanvas.config.grid.right
const top = MyCanvas.config.grid.top
const bottom = MyCanvas.config.grid.bottom
const interval = MyCanvas.config.xAxis.interval
let len = 0
if (MyCanvas.lenTrue) {
len = MyCanvas.lenTrue
} else {
const max = Math.max(...MyCanvas.lxList)
len = Math.ceil(max / interval)
}
// x方向可画范围px
const xw = MyCanvas.canvasWidth - left - right
const jg = xw / len
for (let i = 0; i < len + 1; i++) {
MyCanvas.ctx.moveTo(MyCanvas.canvasWidth - right - (jg * i), MyCanvas.canvasHeight - bottom);
MyCanvas.ctx.lineTo(MyCanvas.canvasWidth - right - (jg * i), top);
}
}
MyCanvas.ctx.stroke();
}
// 绘制y刻度虚线,从下到上画
static yDashedLine() {
const grid = MyCanvas.config.grid
const gridLen = Object.keys(grid).length
MyCanvas.ctx.beginPath();
MyCanvas.ctx.strokeStyle = MyCanvas.config.yAxis.dashedColor;
MyCanvas.ctx.setLineDash([3, 2]);
if (gridLen) {
const left = MyCanvas.config.grid.left
const right = MyCanvas.config.grid.right
const top = MyCanvas.config.grid.top
const bottom = MyCanvas.config.grid.bottom
const max = Math.max(...MyCanvas.ylist)
const interval = MyCanvas.config.yAxis.interval
const len = Math.ceil(max / interval)
const xh = MyCanvas.canvasHeight - top - bottom
const jg = xh / len
for (let i = 0; i < len + 1; i++) {
MyCanvas.ctx.moveTo(left, MyCanvas.canvasHeight - bottom - (jg * i));
MyCanvas.ctx.lineTo(MyCanvas.canvasWidth - right, MyCanvas.canvasHeight - bottom - (jg * i));
}
}
MyCanvas.ctx.stroke();
}
// 绘制y刻度值,从下到上画
static yGraduate() {
const grid = MyCanvas.config.grid
const yAxis = MyCanvas.config.yAxis
const gridLen = Object.keys(grid).length
MyCanvas.ctx.beginPath();
MyCanvas.ctx.strokeStyle = '#000';
MyCanvas.ctx.textAlign = "right";
MyCanvas.ctx.textBaseline = "middle";
MyCanvas.ctx.font = "14px Arial";
if (gridLen) {
const left = MyCanvas.config.grid.left
const right = MyCanvas.config.grid.right
const top = MyCanvas.config.grid.top
const bottom = MyCanvas.config.grid.bottom
const max = Math.max(...MyCanvas.ylist)
const interval = MyCanvas.config.yAxis.interval
const len = Math.ceil(max / interval)
const xh = MyCanvas.canvasHeight - top - bottom
const jg = xh / len
for (let i = 0; i < len + 1; i++) {
if (i == 0) {
MyCanvas.ctx.fillText(0, left - yAxis.kdjl, MyCanvas.canvasHeight - bottom)
} else {
MyCanvas.ctx.fillText(interval * i, left - yAxis.kdjl, MyCanvas.canvasHeight - bottom - (jg * i))
}
}
}
}
}
const myCanvas = document.querySelector('#myCanvas');
const canvas = new MyCanvas()
canvas.init(myCanvas)
canvas.setOption({
grid: {
top: 20,
right: 20,
bottom: 20,
left: 40,
},
yAxis: {
dashedColor: '#000',
kdjl: 8, // 刻度与轴线距离
interval: 5, // 每条虚线间隔(单位不是px,而是数值,对应刻度值距离值)
},
xAxis: {
dashedColor: '#000',
kdjl: 8, // 刻度与轴线距离
interval: 5, // 每条虚线间隔(单位不是px,而是数值,对应刻度值距离值)
},
series: {
curvature: 20,// 曲线曲率单位px
data: [[10, 20, '信号1'], [8, 23, '信号2'], [15, 30, '信号3'], [25, 10, '信号4'], [44, 58, '信号5']]
}
})
</script>
</html>