用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>
引用的话,就在想用的地方,直接引组件用。