vue根据点的位置实现动态连线
最终实现效果

实现代码
<template>
<div class="item-box" ref="dataComparisonBox">
<div class="equipment-box">
<div class="equipment-item equipment1" :style="`left:${equipmentList[0].left}px;top:${equipmentList[0].top}px`">
<div @click="equipmentClick('S1')" style="width: 146px;height: 258px;background-color: #ff0000"></div>
<span>S1</span>
</div>
<div class="equipment-item equipment2" :style="`left:${equipmentList[1].left}px;top:${equipmentList[1].top}px`">
<div @click="equipmentClick('S2')" style="width: 108px;height: 211px;background-color: #fa7300"></div>
<span>S2</span>
</div>
<div class="equipment-item equipment3" :style="`left:${equipmentList[2].left}px;top:${equipmentList[2].top}px`">
<div @click="equipmentClick('C3')" style="width: 95px;height: 173px;background-color: #ffff00"></div>
<span>C3</span>
</div>
<div class="equipment-item equipment4" :style="`left:${equipmentList[3].left}px;top:${equipmentList[3].top}px`">
<div @click="equipmentClick('C4')" style="width: 124px;height: 150px;background-color: #84ff3c"></div>
<span>C4</span>
</div>
<div class="equipment-item equipment5" :style="`left:${equipmentList[4].left}px;top:${equipmentList[4].top}px`">
<div @click="equipmentClick('X5')" style="width: 94px;height: 152px;background-color: #67b1ff"></div>
<span>X5</span>
</div>
<div class="equipment-item equipment6" :style="`left:${equipmentList[5].left}px;top:${equipmentList[5].top}px`">
<div @click="equipmentClick('X6')" style="width: 96px;height: 141px;background-color: #ee00ff"></div>
<span>X6</span>
</div>
<div class="equipment-line">
<div class="line"
:style="`left:${initialPoint.left}px;top:${initialPoint.top}px;transform: rotate(${endPointList[0].angle}deg);width: ${endPointList[0].height}px;`">
<span v-for="(item, index) in endPointList[0].index" :key="index"
:style="{backgroundColor: index % isEqual === 0 ? lineState[0] === '#fff' ? '#fff' : '#666' : lineState[0] }"></span>
</div>
<div class="line"
:style="`left:${initialPoint.left}px;top:${initialPoint.top}px;transform: rotate(${endPointList[1].angle}deg);width: ${endPointList[1].height}px;`">
<span v-for="(item, index) in endPointList[1].index" :key="index"
:style="{backgroundColor: index % isEqual === 0 ? lineState[1] === '#fff' ? '#fff' : '#666' : lineState[1] }"></span>
</div>
<div class="line"
:style="`left:${initialPoint.left}px;top:${initialPoint.top}px;transform: rotate(${endPointList[2].angle}deg);width: ${endPointList[2].height}px;`">
<span v-for="(item, index) in endPointList[2].index" :key="index"
:style="{backgroundColor: index % isEqual === 0 ? lineState[2] === '#fff' ? '#fff' : '#666' : lineState[2] }"></span>
</div>
<div class="line"
:style="`left:${initialPoint.left}px;top:${initialPoint.top}px;transform: rotate(${endPointList[3].angle}deg);width: ${endPointList[3].height}px;`">
<span v-for="(item, index) in endPointList[3].index" :key="index"
:style="{backgroundColor: index % isEqual === 0 ? lineState[3] === '#fff' ? '#fff' : '#666' : lineState[3] }"></span>
</div>
<div class="line"
:style="`left:${initialPoint.left}px;top:${initialPoint.top}px;transform: rotate(${endPointList[4].angle}deg);width: ${endPointList[4].height}px;`">
<span v-for="(item, index) in endPointList[4].index" :key="index"
:style="{backgroundColor: index % isEqual === 0 ? lineState[4] === '#fff' ? '#fff' : '#666' : lineState[4] }"></span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "dataComparison",
components: {},
data() {
return {
_width: '1920',
_height: '1080',
oldEquipmentList: [ // 设计图默认距离
{left: 153, top: 292},
{left: 387, top: 123},
{left: 587, top: 497},
{left: 902, top: 291},
{left: 1256, top: 404},
{left: 1337, top: 102},
],
equipmentList: [ // 计算每个设备的位置
{left: 0, top: 0},
{left: 0, top: 0},
{left: 0, top: 0},
{left: 0, top: 0},
{left: 0, top: 0},
{left: 0, top: 0},
],
initialPointList: { // 所有起始点 left top 连线的起始点 width height 设备宽高 indentY indentX 偏移距离
S1: {
left: 153, top: 292, width: 146, height: 258, indentY: 22, indentX: 114
},
S2: {
left: 387, top: 123, width: 108, height: 211, indentY: 18, indentX: 76
},
C3: {
left: 587, top: 497, width: 95, height: 173, indentY: 30, indentX: 47
},
C4: {
left: 902, top: 291, width: 124, height: 150, indentY: 30, indentX: 62
},
X5: {
left: 1256, top: 404, width: 94, height: 152, indentY: 32, indentX: 36
},
X6: {
left: 1337, top: 102, width: 96, height: 141, indentY: 37, indentX: 40
}
},
initialPoint: {left: 1378, top: 204}, // 当前调用起始点
endPointList: [{}, {}, {}, {}, {}], // 计算剩余点结束位置
interval: null,
lineState: ['#0f0', '#ff0', '#f00', '#fff', '#fff'], // 连线颜色
present: 'S1',
deviceNameList: ['S1', 'S2', 'C3', 'C4', 'X5', 'X6'],
isEqual: 0
}
},
mounted() {
this.setEquipment()
this.rotateFun('S1')
this.interval = setInterval(() => { // 连线动画
if (this.isEqual === 8) {
this.isEqual = 2
} else {
this.isEqual++
}
}, 500)
},
methods: {
// 计算每个设备的位置 -- TODO:设计图宽 1628 高 815
setEquipment() {
this._width = this.$refs.dataComparisonBox.offsetWidth
this._height = this.$refs.dataComparisonBox.offsetHeight
let _w = (this._width / 1628).toFixed(3)
let _h = (this._height / 815).toFixed(3)
this.equipmentList = []
this.oldEquipmentList.forEach((item) => {
this.equipmentList.push({left: item.left * _w, top: item.top * _h})
})
},
// 线段角度计算
rotateFun(val) {
this._width = this.$refs.dataComparisonBox.offsetWidth
this._height = this.$refs.dataComparisonBox.offsetHeight
let _w = (this._width / 1628).toFixed(2)
let _h = (this._height / 815).toFixed(2)
this.endPointList = []
// 获取线段的起始结束位置
for (const valKey in this.initialPointList) {
if (valKey !== val) {
let left = this.initialPointList[valKey].left * _w + this.initialPointList[valKey].indentX * _w
let top = this.initialPointList[valKey].top * _h + this.initialPointList[valKey].height - this.initialPointList[valKey].indentY * _h
this.endPointList.push({left: left, top: top})
} else {
let left = this.initialPointList[valKey].left * _w + this.initialPointList[valKey].indentX * _w
let top = this.initialPointList[valKey].top * _h + this.initialPointList[valKey].height - this.initialPointList[valKey].indentY * _h
this.initialPoint.left = left
this.initialPoint.top = top
}
}
// 获取角度值
this.endPointList.forEach((item) => {
let x = ''
let y = ''
x = item.left - this.initialPoint.left
y = this.initialPoint.top - item.top
let c1 = 0;
let c2 = Math.atan2(x / _w, y / _h) * 180 / (Math.PI);
let angle;
c1 = c1 <= -90 ? (360 + c1) : c1;
c2 = c2 <= -90 ? (360 + c2) : c2;
// TODO 夹角获取
angle = c2 - c1;
angle = angle < 0 ? angle + 360 : angle;
item.angle = angle - 90
// TODO 计算两点之间距离
const base = Math.abs(y) / _h;
const perpendicular = Math.abs(x) / _w;
const findHypotenuse = (base, perpendicular) => {
const bSquare = base * base;
const pSquare = perpendicular * perpendicular;
const sum = bSquare + pSquare;
return Math.sqrt(sum)
};
let hypotenuse = findHypotenuse(base, perpendicular)
hypotenuse = hypotenuse * _w
item.height = hypotenuse
item.index = Math.round(hypotenuse / 20)
})
},
equipmentClick(val) {
this.present = val
this.rotateFun(val)
}
}
}
</script>
<style lang="scss" scoped>
.item-box {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: 100%;
background-position: center;
// 设计图宽 1628 高 815
.equipment-box {
width: 100%;
height: 100%;
position: relative;
.equipment-item {
width: auto;
height: auto;
position: absolute;
left: 0;
top: 0;
z-index: 99;
span {
position: absolute;
bottom: -30px;
left: 50%;
font-size: 18px;
}
}
.equipment-line {
width: 100%;
height: 100%;
position: relative;
.line {
width: 1px;
height: 1px;
position: absolute;
cursor: pointer;
transform-origin: bottom left;
display: flex;
justify-content: space-between;
span {
display: inline-block;
width: 8px;
height: 3px;
border-radius: 10px;
background-color: #ffffff;
}
}
}
}
}
</style>

浙公网安备 33010602011771号