使用Canvas绘制一个自适应长度的折线图

news2024/9/27 5:53:05

要求x轴根据数据长度自适应
y轴根据数据最大值取长度值
在这里插入图片描述

<template>
  <div ref="cvsContainer" class="cvs-container">
    <canvas ref="cvs" class="canvas"></canvas>
  </div>
</template>

<script setup>
import {computed, defineProps, onMounted, ref} from "vue";

onMounted(() => {
  initLine()
})

const data = defineProps({
  list: {
    type: Array,
    default: () => [1,2,3,4,5,6,7,8,9,10]
  }
});

const max = computed(() => {
  return Math.max(...data.list)
})
console.log(max.value)
const dataSize = computed(() => {
  return data.list.length
})
console.log(dataSize.value)

const cvs = ref(null);
const cvsContainer = ref(null);
const initLine = () => {
  const container = cvsContainer.value
  const width = container.offsetWidth
  const height = container.offsetHeight
  cvs.value.width = width
  cvs.value.height = height
  const ctx = cvs.value.getContext('2d')
  ctx.beginPath()
  ctx.moveTo(50, 50)
  ctx.lineTo(50, 400)
  ctx.lineTo(600, 400)
  ctx.stroke()

  // 画x刻度
  // x轴总长550,我们用数据总长度计算出每个刻度要画多长,这里刻度数量=数据长度,例如10cm想要分成2个刻度,那就是开头和结束两个刻度,就等于10个要分成一段也就是10/(2-1)
  const xScale = 550 / (dataSize.value-1);
  for (let i = 1; i <= dataSize.value; i++) {
    ctx.beginPath()
    ctx.strokeStyle = 'blue'
    ctx.lineWidth = 1
    ctx.moveTo(50 + i * xScale, 400)
    ctx.lineTo(50 + i * xScale, 390)
    ctx.stroke()
    ctx.fillText(i, 47 + (i-1) * xScale, 415,)
  }

  // 画y刻度
  // (我们只显示7个y轴刻度)我们计算出最大值分成7份每份有多长
  const yScale = (max.value / 7).toFixed(0);
  console.log('yScale', yScale)
  for (let i = 0; i <= 7; i++) {
    ctx.beginPath()
    ctx.strokeStyle = 'blue'
    ctx.lineWidth = 1
    ctx.moveTo(50, 400 - i * 50)
    ctx.lineTo(60, 400 - i * 50)
    ctx.stroke()
    ctx.fillText(i * yScale, 50 - max.value.toString().length * 8, 403 - i * 50,)
  }

  //  画折线
  for (let i = 0; i < data.list.length; i++) {
    setTimeout(() => {
      ctx.beginPath()
      ctx.strokeStyle = 'red'
      ctx.lineWidth = 1
      ctx.moveTo(50 + i * xScale, 400 - data.list[i] * 350 / max.value)
      ctx.lineTo(50 + (i + 1) * xScale, 400 - data.list[i + 1] * 350 / max.value)
      ctx.stroke()
      ctx.fillText(data.list[i], 45 + i * xScale, 400 - data.list[i] * 350 / max.value,)
    }, 500 / data.list.length * i)
  }
}
</script>

<style lang="scss" scoped>
.cvs-container {
  width: 1200px;
  height: 800px;
  background-color: white;
  border-radius: 15px;

  .canvas {

  }
}

</style>

增加了鼠标移动的数值提示框

在这里插入图片描述

<template>
  <div ref="cvsContainer" class="cvs-container">
    <canvas ref="cvs" class="canvas"></canvas>
    <div v-show="pageData.pointerShow" class="pointer" :style="`left:${pageData.pointerX}px;top: ${pageData.pointerY}px;`">
      {{pageData.pointer}}
    </div>
  </div>
</template>

<script setup>
import {computed, defineProps, onMounted, reactive, ref} from "vue";

onMounted(() => {
  initLine()
})

const data = defineProps({
  list: {
    type: Array,
    default: () => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  xAxios: {
    type: Array,
    default: () => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }
});

const pageData = reactive({
  pointer:0,
  pointerX:0,
  pointerY:0,
  pointerShow:false
});

const max = computed(() => {
  return Math.max(...data.list)
})
console.log(max.value)


const cvs = ref(null);
const cvsContainer = ref(null);
const initLine = () => {
  const container = cvsContainer.value
  const width = container.offsetWidth
  const height = container.offsetHeight
  cvs.value.width = width
  cvs.value.height = height
  const ctx = cvs.value.getContext('2d')
  ctx.beginPath()
  ctx.moveTo(50, 50)
  ctx.lineTo(50, 400)
  ctx.lineTo(600, 400)
  ctx.stroke()

  // 画x刻度
  // x轴总长550,我们用数据总长度计算出每个刻度要画多长,这里刻度数量=数据长度,例如10cm想要分成2个刻度,那就是开头和结束两个刻度,就等于10个要分成一段也就是10/(2-1)
  const xScale = 550 / (data.xAxios.length - 1);
  for (let i = 0; i < data.xAxios.length; i++) {
    ctx.beginPath()
    ctx.strokeStyle = '#66666666'
    ctx.lineWidth = 0.5
    ctx.moveTo(50 + i * xScale, 400)
    ctx.lineTo(50 + i * xScale, 50)
    ctx.stroke()
    ctx.fillText(data.xAxios[i], 47 + i * xScale, 415,)
  }

  // 画y刻度
  // (我们只显示7个y轴刻度)我们计算出最大值分成7份每份有多长
  const yScale = (max.value / 7);
  console.log('yScale', yScale)
  for (let i = 0; i <= 7; i++) {
    ctx.beginPath()
    ctx.strokeStyle = '#66666666'
    ctx.lineWidth = 0.5
    ctx.moveTo(50, 400 - i * 50)
    ctx.lineTo(600, 400 - i * 50)
    ctx.stroke()
    ctx.fillText((i * yScale).toFixed(0), 50 - max.value.toString().length * 8, 403 - i * 50,)
  }

  //  画折线
  for (let i = 0; i < data.list.length; i++) {
    // 这里使用定时器渲染,模拟动画
    setTimeout(() => {
      ctx.beginPath()
      ctx.strokeStyle = 'rgba(31,121,211,.7)'
      ctx.lineWidth = 2
      ctx.moveTo(50 + i * xScale, 400 - data.list[i] * 350 / max.value)
      ctx.lineTo(50 + (i + 1) * xScale, 400 - data.list[i + 1] * 350 / max.value)
      ctx.stroke()
      // 字体颜色
      ctx.fillText(data.list[i], 45 + i * xScale, 400 - data.list[i] * 350 / max.value)
    }, 500 / data.list.length * i) // 渲染总时长/数据长度=每个数据渲染时长,使用定时器模拟动画
  }

  // 获取元素的边界信息
  const rect = cvs.value.getBoundingClientRect();
  // 绑定鼠标移动事件
  cvs.value.addEventListener('mousemove', (e) => {
    // 计算鼠标在元素内部的相对位置
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    // 打印出相对位置
    if (x>50){
      const index = Math.round((x - 50) / xScale);
      // 四舍五入
      // 获取当前点的数据
      const value = data.list[index];
      // 更新提示框的值
      pageData.pointer = value
      pageData.pointerX=x+15
      pageData.pointerY=y+15
    }
  })
  cvs.value.addEventListener('mouseleave', () => {
    pageData.pointerShow = false;
  })
  cvs.value.addEventListener('mouseenter',()=>{
    pageData.pointerShow = true;
  })
}
</script>

<style lang="scss" scoped>
.cvs-container {
  width: 1200px;
  height: 800px;
  background-color: white;
  border-radius: 15px;
  position: relative;
  .canvas {

  }
  .pointer{
    position: absolute;
    width: 100px;
    height: 50px;
    font-size: 24px;
    border-radius: 10px;
    background-color: #0675c5;
    display: flex;
    justify-content: center;
    align-items: center;
    color: white;
    box-shadow: 3px 3px 6px  rgba(0, 0, 0, 0.3);
  }
}

</style>

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

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

相关文章

188基于matlab的AR模型参数估计

基于matlab的AR模型参数估计&#xff0c;burg法和ule-Walker法估计信号&#xff0c;并输出估计误差。程序已调通&#xff0c;可直接运行。 188 AR模型参数估计 burg法和ule-Walker法 (xiaohongshu.com)

离散数学例题——6.树和特殊图

树 树的证明 森林 同构树非同构树 生成树 有向树 二叉树遍历 哈夫曼树 特殊图 欧拉图&#xff08;一次边&#xff09; Fleury算法求欧拉回路 欧拉通路&#xff08;一笔画&#xff09; 哈密顿图&#xff08;一次点&#xff09; 哈密顿图的充分条件 哈密顿图必要条件 二部图 二部…

人工智能OCR领域安全应用措施

引言 编写目的 随着新一轮科技革命和产业变革的深入发展&#xff0c;5G、大数据、云计算、深度学习等新技术日益成为推动社会进步的核心动力。人工智能&#xff08;AI&#xff09;作为这些新技术的集大成者&#xff0c;正迅速成为新型基础设施建设的战略性支柱&#xff0c;其广…

【详识C语言】自定义类型之三:联合

本章重点 联合 联合类型的定义 联合的特点 联合大小的计算 联合&#xff08;共用体&#xff09; 联合类型的定义 联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09;…

AI 辅助研发趋势

引言 随着人工智能技术的持续发展与突破&#xff0c;AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&#xff0c;AI辅助研发不仅…

Linux学习——锁

目录 ​编辑 一&#xff0c;锁的概念 二&#xff0c;锁的操作 1&#xff0c;锁类型 pthread_mutex_t 2&#xff0c;初始化锁 3&#xff0c;上锁 4&#xff0c;解锁 5&#xff0c;销毁锁 三&#xff0c;线程安全问题演示 四&#xff0c;锁的原理 五&#xff0c;死锁 …

每日OJ题_牛客HJ73 计算日期到天数转换(IO型OJ)

目录 牛客HJ73 计算日期到天数转换 解析代码 牛客HJ73 计算日期到天数转换 计算日期到天数转换_牛客题霸_牛客网 解析代码 #include <iostream> using namespace std; int main() {int year 0, month 0, day 0, sum 0;cin >> year >> month >>…

【SpringBoot框架篇】36.整合Tess4J搭建提供图片文字识别的Web服务

文章目录 简介文件下载引入依赖main函数中使用基于Springboot搭建OCR Web服务配置traineddata路径枚举用到的语种类型定义接口响应的json数据格式封装OCR服务引擎编写web提供服务的接口启动服务并且测试html demo扩展 项目配套代码 简介 Tess4J是一个基于Tesseract OCR引擎的J…

[Java安全入门]三.URLDNS链

一.前言 在初步学习java的序列化和反序列化之后&#xff0c;这里学习java反序列化漏洞的一个利用链&#xff0c;也是比较基础的一条链。 由于URLDNS不需要依赖第三方的包&#xff0c;同时不限制jdk的版本&#xff0c;所以通常用于检测反序列化的点。 二.代码展开分析 构造链 …

Learn OpenGL 03 着色器

GLSL 着色器的开头总是要声明版本&#xff0c;接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数&#xff0c;在这个函数中我们处理所有的输入变量&#xff0c;并将结果输出到输出变量中。 一个典型的着色器有下面的结构&#xff1a; #version vers…

[java入门到精通] 10 常用API , 正则表达式 , Collection集合

今日目标 BigInteger类BigDecimal类Arrays类包装类String类的常用方法正则表达式Collection集合 1 BigInteger类 1.1 概述 概述 : java.math.BigInteger类是一个引用数据类型 , 可以用于计算一些大的整数 , 当超出基本数据类型数据范围的整数运算时就可以使用BigInteger了。…

Arduino Uno使用Mind+实现图形化编程

文章目录&#xff1a; 一&#xff1a;软件下载安装 1.下载安装 1.1 开发软件 2.辅助软件 2.主控板 二&#xff1a;基础 1.LED 2.传感器 3.智能小车 三&#xff1a;学习资源 一&#xff1a;软件下载安装 1.下载安装 1.1 开发软件 Arduino IDE代码编程软件&#…

集合和数组的相关操作

目录 1.数组转集合(引用类型数组) 2.数组转集合(基础类型数组) 3.集合转数组 4.集合之间是否相交 5.获取两个集合的交集 6.集合转为字符串 1.数组转集合(引用类型数组) (1)Arrays.asList 示例&#xff1a; String[] colArr new String[6];colArr[0] "1";co…

Fastgithub

上Github太慢、打不开怎么办&#xff1f; 选择之一是Fastgithub工具&#xff0c;同时支持win, linux, mac。 1. 工作原理 从公共dns服务器拿到github的大量ip数据&#xff0c;检测哪些ip可用&#xff0c;哪些ip访问速度最佳&#xff0c;然后编写一个本地版的dns服务&#xff0…

小巧设备,大能量:探索口袋中的远程控制神器

在这个科技日新月异的时代&#xff0c;我们的生活被各种手机软件所包围。几乎每个人都有一个甚至多个手机&#xff0c;你是否也有遇到过需要远程操作自己某一台手机的场景呢&#xff1f;今天&#xff0c;我要向大家推荐一款神奇的手机远程操作神器&#xff0c;让你可以随时随地…

【EtherCAT实践篇】十、SSC工具使用说明

EtherCAT Slave Stack Code&#xff08;SSC&#xff09;是倍福提供的EtherCAT从站源代码生成工具&#xff0c;基于SSC工具&#xff0c;可以大大降低EtherCAT数据通讯程序及xml设计难度。 本操作参考SSC软件包中的EtherCAT Slave Design Quick Guide.pdf文档。 1、创建一个SSC工…

项目解决方案:视频监控接入和录像系统设计方案(下)

目 录 1.概述 2. 建设目标及需求 2.1建设总目标 2.2 需求描述 ​2.3 需求分析 3.设计依据与设计原则 3.1设计依据 3.2 设计原则 4.建设方案设计 4.1系统方案设计 4.2组网说明 5.产品介绍 5.1视频监控综合资源管理平台介绍 5.2视频录像服务器和存储 5.2.…

后勤管理系统|基于SSM 框架+vue+ Mysql+Java+B/S架构技术的后勤管理系统设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

目录 文末获取源码 前台首页功能 员工注册、员工登录 个人中心 公寓信息 员工功能模块 员工积分管理 管理员登录 ​编辑管理员功能模块 个人信息 ​编辑员工管理 公寓户型管理 ​编辑公寓信息管理 系统结构设计 数据库设计 luwen参考 概述 源码获取 文末获取源…

bug总结(1)--变量取错

a c t i v i t y [ ′ t a g n a m e ′ ] 应为 activity[tag_name]应为 activity[′tagn​ame′]应为couponActivitList[0][‘name’] .隐藏的bug&#xff0c;在测试中竟然测不出来&#xff0c;而且上线了好久。为啥会出现这种低级错误呢&#xff1f;第一是写的时候不够仔细认…

C语言:基于单链表实现的泊车管理系统

一、需求 &#xff08;1&#xff09;管理员方账号登录&#xff1b; &#xff08;2&#xff09;车位管理显示&#xff1a;车位状态&#xff1b; &#xff08;3&#xff09;收费管理&#xff1a;小轿车 5元/小时&#xff0c;面包车6元/小时&#xff0c;大货车或客车7元/小时&a…