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>

 

posted @ 2023-02-10 16:46  果茶加冰  阅读(2240)  评论(0)    收藏  举报