先上图片!
为了记录下,智能电表,以及智能设备的监测。
正常数据应该从后端来,我为了本地展示,把数据搬前端来,后端代码就不传了。
上代码:
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 = null
async 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 = imaged3
console.log(deviceImgd2, deviceImgd3)
}
function drawTree(node, x, y, level) {
const imgWidth = 25
const imgHeight = 25
const hGap = 200
// const vGap = 200
console.log('drawTree方法')
console.log(node)
// Draw current node
if (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 child
ctx.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.value
const containerEl = container.value
const devicePixelRatio = window.devicePixelRatio || 1
const rect = containerEl.getBoundingClientRect()
canvasEl.width = rect.width * devicePixelRatio
canvasEl.height = rect.height * devicePixelRatio
canvasEl.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.1
if (e.deltaY < 0) {
scale *= zoomFactor
} else {
scale /= zoomFactor
}
draw()
}
function startDragging(e) {
isDragging = true
lastX = e.clientX
lastY = e.clientY
}
function stopDragging() {
isDragging = false
}
function onMouseMove(e) {
if (!isDragging) return
const dx = e.clientX - lastX
const dy = e.clientY - lastY
offsetX += dx
offsetY += dy
lastX = e.clientX
lastY = e.clientY
draw()
}
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>
引用的话,就在想用的地方,直接引组件用。