canvas 曲线图 双数值轴 山峰图

news2024/11/25 1:00:38

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

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1194618.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从单体到微服务:使用Spring Boot构建事件驱动的Java应用程序

Spring Boot是Pivotal团队设计的一种微服务框架&#xff0c; 基于Spring开发&#xff0c;用于简化新Spring应用的初始搭建及开发过程&#xff0c;提升Spring 开发者的体验。它秉持“约定大于配置”的思想&#xff0c;集成了大量开箱即用的第三方库&#xff0c;支持绝大多数开源…

HCIP---VRRP

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一. VRRP概述 VRRP---虚拟路由器冗余协议 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff09;是一种用于在多个路由器之间创建虚拟路由器的协议。 VRRP使用了一系列协议来实现路…

win11 idea 错误: 找不到或无法加载主类

买了新电脑win11系统&#xff0c;配置环境之后运行项目&#xff0c;始终运行不起来&#xff0c;一直报 刚开始以为是环境没装好&#xff0c;但是我创建其他项目运行时是正常的 纠结了好久突然发现&#xff0c;是不是因为项目路径中有中文造成的找不到编译文件 最后把项目改为…

【Python】AppUI自动化—appium自动化元素定位、元素事件操作(17)下

文章目录 前言一.Appium 元素定位1.定位方式种类2.如何定位2.1 id定位2.2 className定位2.3 content-desc 定位2.4 Android Uiautomator定位4.1 text定位4.2 text模糊定位4.3 text正则匹配定位4.4 resourceId定位4.5 resourceId正则匹配定位4.6 className定位4.7 className正则…

P6入门:项目初始化2-项目详情之日期Date

前言 使用项目详细信息查看和编辑有关所选项目的详细信息&#xff0c;在项目创建完成后&#xff0c;初始化项目是一项非常重要的工作&#xff0c;涉及需要设置的内容包括项目名&#xff0c;ID,责任人&#xff0c;日历&#xff0c;预算&#xff0c;资金&#xff0c;分类码等等&…

RPC 框架 openfeign 介绍和学习使用总结

一、基本概念 RPC 远程过程调用&#xff08;Remote Procedure Call&#xff09;的缩写形式 Birrell 和 Nelson 在 1984 发表于 ACM Transactions on Computer Systems 的论文《Implementing remote procedure calls》对 RPC 做了经典的诠释。 RPC 是指计算机 A 上的进程&am…

元核云亮相金博会,智能质检助力金融合规

11月初&#xff0c;第五届中新&#xff08;苏州&#xff09;数字金融应用博览会&#xff5c;2023金融科技大会在苏州国际博览中心举办&#xff0c;围绕金融科技发展热点领域及金融行业信息科技领域重点工作&#xff0c;分享优秀实践经验&#xff0c;探讨数字化转型路径与未来发…

C# OpenCvSharp DNN HybridNets 同时处理车辆检测、可驾驶区域分割、车道线分割

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Numerics; using System.Text; using System.Windows.Forms;namespace OpenCvSharp_D…

设备零部件更换ar远程指导系统加强培训效果

随着科技的发展&#xff0c;AR技术已经成为了一种广泛应用的新型技术。AR远程指导系统作为AR技术的一种应用&#xff0c;具有非常广泛的应用前景。 一、应用场景 气象监测AR教学软件适用于多个领域&#xff0c;包括气象、环境、地理等。在教学过程中&#xff0c;软件可以帮助学…

Day26力扣打卡

打卡记录 搜索旋转排序数组&#xff08;二分&#xff09; 链接 class Solution {int findMin(vector<int> &nums) {int left -1, right nums.size() - 1; // 开区间 (-1, n-1)while (left 1 < right) { // 开区间不为空int mid left (right - left) / 2;if…

Antd G6实现自定义工具栏

在使用g6实现知识图谱可视化中&#xff0c;产品经理提出了有关图谱操作的不少功能&#xff0c;需要放置在工具栏中&#xff0c;其中有些功能不在g6自带的功能里&#xff0c;且工具栏样式、交互效果也和官方自定义工具栏不同。那我们怎么去实现呢&#xff1f; g6官方的工具栏案例…

Azure 机器学习 - 使用自动化机器学习训练计算机视觉模型的数据架构

目录 一、用于训练的数据架构图像分类&#xff08;二进制/多类&#xff09;多标签图像分类对象检测实例分段 二、用于联机评分的数据架构输入格式输出格式图像分类&#xff08;二进制/多类&#xff09;多标签图像分类对象检测实例分段 在线评分和可解释性 (XAI) 的数据格式支持…

Linux的目录的权限

目录 目录的权限 目录的权限 1、可执行权限: 如果目录没有可执行权限, 则无法cd到目录中. 2、可读权限: 如果目录没有可读权限, 则无法用ls等命令查看目录中的文件内容. 3、可写权限: 如果目录没有可写权限, 则无法在目录中创建文件, 也无法在目录中删除文件. 上面三个权限是…

SpringBoot3+Vue3+Mysql+Element Plus完成数据库存储blob类型图片,前端渲染后端传来的base64类型图片

前言 如果你的前后端分离项目采用SpringBoot3Vue3Element Plus&#xff0c;且在没有OSS&#xff08;对象存储&#xff09;的情况下&#xff0c;使用mysql读写图片&#xff08;可能不限于图片&#xff0c;待测试&#xff09;。 耗时三天&#xff0c;在踩了无数雷后&#xff0c…

深度学习 python opencv 动物识别与检测 计算机竞赛

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

如何帮助 3D CAD 设计师实现远程办公

当 3D CAD 设计师需要远程办公时&#xff0c;他们可能需要更强的远程软件&#xff0c;以满足他们的专业需求。比如高清画质&#xff0c;以及支持设备重定向、多显示器支持等功能。3D CAD 设计师如何实现远程办公&#xff1f;接下来我们跟随 Platinum Tank Group 的故事来了解一…

百度王颖:百度文库以AI创作能力突破语言边界,促进思想碰撞和文化融通

1月9日&#xff0c;2023年世界互联网大会乌镇峰会“网络传播与文明交流互鉴论坛”召开。百度副总裁、互娱和垂类平台负责人王颖出席并发表“以技术搭建跨文化交流桥梁”主题演讲。她表示&#xff0c;在大模型的加持下&#xff0c;百度各个产品都在重构&#xff0c;通过技术助力…

链表练习题

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

【数据结构】反射、枚举

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈数据结构 &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 反射、枚举 1. 反射1.1 定义1.2 反射…

建造者模式 rust和java的实现

文章目录 建造者模式介绍优点缺点使用场景 实现javarust rust代码仓库 建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象。 一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 介绍…