当前位置: 首页 > ops >正文

用vue canvas画一个能源电表和设备的监测图

先上图片!

为了记录下,智能电表,以及智能设备的监测。

正常数据应该从后端来,我为了本地展示,把数据搬前端来,后端代码就不传了。

上代码:

TreeCanvas.vue

<template><div class="tree-container" ref="container" @wheel.prevent="handleWheel"><canvas ref="canvas" @mousedown="startDragging" @mousemove="onMouseMove" @mouseup="stopDragging"@mouseleave="stopDragging"></canvas></div>
</template><script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'const canvas = ref(null)
const ctx = ref(null)
const container = ref(null)// 树形数据结构
const treeData = {id: 1,name: '别墅总电表',type: 1,children: [{id: 2,name: 'F-1健身房',type: 2,error: 0,children: [{id: 4,name: '电表-器械区',type: 1,children: [{id: 8,name: '跑步机',type: 2,error: 0,}, {id: 9,name: '划船机',type: 2,error: 1,},{id: 100,name: '动感单车',type: 2,error: 0,},]},{id: 7,name: '电表-娱乐圈',type: 1,children: [{id: 10,name: '豪华影音室',type: 2,error: 0,children: [{id: 11,name: '电表03',type: 1,error: 0,children: [{id: 12,name: 'ktv运行设备',type: 2,error: 0,},{id: 13,name: '电竞房运行设备',type: 2,error: 0,}]}]},]}]},{id: 3,name: 'F-2',type: 2,error: 0,},{id: 5,name: 'F- -1宝马充电桩',type: 2,error: 1,}]
}let scale = 1
let offsetX = 0
let offsetY = 0
let isDragging = false
let lastX = 0
let lastY = 0
let deviceImg = null
let deviceImgd2 = null
let deviceImgd3 = nullasync function loadDeviceImage() {//加载3种图片// 1 创建 Image 对象const image = new Image()// 2 引入图片image.src = require('@/assets/d.png')deviceImg = image// 1 创建 Image 对象const imaged2 = new Image()// 2 引入图片imaged2.src = require('@/assets/d2.png')deviceImgd2 = imaged2// 1 创建 Image 对象const imaged3 = new Image()// 2 引入图片imaged3.src = require('@/assets/d3.png')deviceImgd3 = imaged3console.log(deviceImgd2, deviceImgd3)
}function drawTree(node, x, y, level) {const imgWidth = 25const imgHeight = 25const hGap = 200// const vGap = 200console.log('drawTree方法')console.log(node)// Draw current nodeif (deviceImg) {if (node.type == 1) {ctx.value.drawImage(deviceImgd3, x - imgWidth / 2, y - imgHeight / 2, imgWidth, imgHeight)} else if (node.type == 2) {if (node.error == 1) {ctx.value.drawImage(deviceImg, x - imgWidth / 2, y - imgHeight / 2, imgWidth, imgHeight)} else if (node.error == 0) {ctx.value.drawImage(deviceImgd2, x - imgWidth / 2, y - imgHeight / 2, imgWidth, imgHeight)}}}ctx.value.fillStyle = '#000'ctx.value.font = '12px sans-serif'ctx.value.textAlign = 'center'//  node.id;var text = ""if (node.error == 1) {text = node.name + "异常"} else {text = node.name}ctx.value.fillText(text, x, y + imgHeight / 2 + 15)// if (node.children && node.children.length > 0) {//     let totalHeight = (node.children.length - 1) * vGap//     let startY = y - totalHeight / 2//     node.children.forEach((child, i) => {//         const childX = x + hGap//         const childY = startY + i * vGap//         // Draw line to child//         ctx.value.beginPath()//         ctx.value.moveTo(x, y)//         ctx.value.lineTo(childX, childY)//         ctx.value.stroke()//         drawTree(child, childX, childY, level + 1)//     })// }if (node.children && node.children.length > 0) {let angleStep = Math.PI / (node.children.length + 1) // 角度步长for (let i = 0; i < node.children.length; i++) {let angle = -(Math.PI / 2) + (i + 1) * angleStep // 计算角度let childX = x + hGap * Math.cos(angle) // 根据角度计算新位置let childY = y + hGap * Math.sin(angle)// Draw line to childctx.value.beginPath()ctx.value.moveTo(x, y)ctx.value.lineTo(childX, childY)ctx.value.stroke()drawTree(node.children[i], childX, childY, level + 1)}}
}function draw() {const canvasEl = canvas.valueconst containerEl = container.valueconst devicePixelRatio = window.devicePixelRatio || 1const rect = containerEl.getBoundingClientRect()canvasEl.width = rect.width * devicePixelRatiocanvasEl.height = rect.height * devicePixelRatiocanvasEl.style.width = `${rect.width}px`canvasEl.style.height = `${rect.height}px`ctx.value = canvasEl.getContext('2d')ctx.value.setTransform(scale * devicePixelRatio, 0, 0, scale * devicePixelRatio, offsetX * devicePixelRatio, offsetY * devicePixelRatio)// ctx.value.clearRect(0, 0, canvasEl.width, canvasEl.height)ctx.value.clearRect(0, 0, canvasEl.width, canvasEl.height)console.log('draw方法')if (deviceImg) {drawTree(treeData, 100, rect.height / 2, 0)}
}function handleWheel(e) {const zoomFactor = 1.1if (e.deltaY < 0) {scale *= zoomFactor} else {scale /= zoomFactor}draw()
}function startDragging(e) {isDragging = truelastX = e.clientXlastY = e.clientY
}function stopDragging() {isDragging = false
}function onMouseMove(e) {if (!isDragging) returnconst dx = e.clientX - lastXconst dy = e.clientY - lastYoffsetX += dxoffsetY += dylastX = e.clientXlastY = e.clientYdraw()
}function resizeHandler() {draw()
}onMounted(async () => {await loadDeviceImage()draw()window.addEventListener('resize', resizeHandler)
})onBeforeUnmount(() => {window.removeEventListener('resize', resizeHandler)
})
</script><style scoped>
.tree-container {width: 100%;height: 600px;overflow: hidden;border: 1px solid #ccc;background-color: #f9f9f9;
}canvas {display: block;cursor: grab;
}
</style>

引用的话,就在想用的地方,直接引组件用。

http://www.xdnf.cn/news/8071.html

相关文章:

  • Python的切片操作[::-1]
  • 集团企业数字化转型及集团数字化整体蓝图未来五年实施方案
  • 【图像处理入门】1. 数字图像的本质:从像素到色彩模型
  • 【计算机网络】网络层——IP协议
  • Java大厂面试:JVM调优、高并发订单处理与大数据服务场景解析
  • leetcode hot100刷题日记——9.矩阵置零
  • PYTORCH_CUDA_ALLOC_CONF基本原理和具体示例
  • 解决leetcode第3548题.等和矩阵分割II
  • asp.net core 添加 EntityFrame
  • 并发编程之并发容器类
  • [Java恶补day3] 128. 最长连续序列
  • 【愚公系列】《Manus极简入门》054-家庭冲突调解师:“家庭和谐使者”
  • 使用 Docker 搭建 PyWPS 2.0 服务全流程详解
  • Oracle 的V$ACTIVE_SESSION_HISTORY 视图
  • XC3588H搭载国产麒麟系统可用于政务/社保一体机吗?
  • 小球弹弹弹
  • 企业级AI搜索解决方案:阿里云AI搜索开放平台
  • 《数据资产价值与收益分配评价模型》
  • 计算机操作系统(十一)调度器/调度程序,闲逛调度与调度算法的评价指标
  • 杰发科技AC7840——CSE硬件加密模块使用(1)
  • JVM——内存模型
  • Starrocks的CBO基石--统计信息的来源 StatisticAutoCollector
  • 前端vscode学习
  • VLLM在linux下部署
  • 智联物联RG3000边缘计算网关助力智慧城市建设
  • 《C 语言字符串操作从入门到实战(下篇):strncpy/strncat/strstr 等函数原理与实现》
  • 《Android 应用开发基础教程》——第十四章:Android 多线程编程与异步任务机制(Handler、AsyncTask、线程池等)
  • uni-app 排坑
  • Maven Profile中的资源过滤与属性管理
  • 华为2025年校招笔试手撕真题教程(三)