canvas 曲线图 双数值轴 山峰图

下面的代码本人亲自撰写,原生不易啊。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xl__qd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值