he wei
2025-06-03 a10f3b82e33756ed0cd62a0cbe83bab8674df16f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<script setup>
import { ref, computed } from "vue";
 
const props = defineProps({
    offset: {
        type: Array,
        default: () => [0, 0],
    },
  /**
   * 每一段路径一个对象
   * {
   *   points: [],
   *   点之前是否需要圆角  个数比points少两个 圆角为true
   *   roundCornerFlags: [],
   * }
   */
    lineList: {
        type: Array,
        default: () => [],
    },
    color: {
        type: String,
        // default: "#67aa57",
        default: "#fff",
    },
    lineWidth: {
        type: Number,
        default: 16,
    },
  r: {
    type: [Number, String],
    default: 6
  },
});
 
function calcVectorProperties(A, B) {
  let [x0, y0] = A;
  let [x1, y1] = B;
  // 计算向量分量
  const dx = x1 - x0;
  const dy = y1 - y0;
  
  // 计算向量的模长(欧几里得距离)
  const magnitude = Math.sqrt(dx * dx + dy * dy);
  if (magnitude < props.r) {
    throw new Error('两点之间距离过近');
  }
  
  // 处理零向量情况(避免除以零)
  const normalizedVector = magnitude === 0 
      ? [0, 0]
      : [ dx / magnitude, dy / magnitude ];
  
  // 计算向量与x轴正方向的夹角(弧度)
  // Math.atan2 返回值范围:[-π, π]
  const angleRadians = Math.atan2(normalizedVector[1], normalizedVector[0]);
 
  // AB上离B点的距离r距离的点的坐标(x2, y2)
  const x2 = x1 - props.r * normalizedVector[0];
  const y2 = y1 - props.r * normalizedVector[1];
  
  return {
    x2,
    y2
  };
}
 
const pathList = computed(() => {
  let list = props.lineList;
  let res = [];
  for (let i = 0, len = list.length; i < len; i++) {
    let { points, roundCornerFlags, lineColor } = list[i];
    let path = `M ${points[0].join(",")} `;
    for (let j = 1, len2 = points.length; j < len2; j++) {
      let str = '';
      let p0 = points[j - 1],
      p1 = points[j],
      p2 = points[j + 1];
 
      if (j < len2 - 1 &&  roundCornerFlags[j - 1]) {
        let { x2, y2 } = calcVectorProperties(p0, p1);
        let { x2: x3, y2: y3 } = calcVectorProperties(p2, p1);
        str = ` L ${x2},${y2} Q ${p1.join(",")} ${x3},${y3} `;
      } else {
        str = ` L ${points[j].join(",")} `
      }
      path += str;
    }
    res.push({path, lineColor});
  }
  return res;
});
 
</script>
 
<template>
  <g ref="g" :transform="'translate(' + offset.join(',') + ')'">
    <path v-for="(item, index) in pathList" :key="'path0_' + index"
      fill="none"
      :d="item['path']"
      :stroke-width="2"
      :stroke="item['lineColor']"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <animate attributeName="stroke-width" values="2;8;2" dur="2s" repeatCount="indefinite" />
      <animate attributeName="opacity" values="1;0.4;1" dur="2s" repeatCount="indefinite" />
    </path>
    <path v-for="(item, index) in pathList" :key="'path1_' + index"
      fill="none"
      :d="item['path']"
      :stroke-width="lineWidth - 4"
      stroke="rgba(255,255,255, 0.4)"
      stroke-linecap="round"
      stroke-linejoin="round"
        stroke-dasharray="18 142"
        stroke-dashoffset="174"
    >
      <animate attributeName="stroke-dashoffset" from="174" to="14" dur="4s" repeatCount="indefinite" />
      <animate attributeName="stroke-width" :values="`${lineWidth - 8};${lineWidth + 4};${lineWidth - 8}`" dur="2s" repeatCount="indefinite" />
    </path>
    <path v-for="(item, index) in pathList" :key="'path2_' + index"
       :d="item['path']" :stroke="color" :stroke-width="lineWidth"
        fill="none"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-dasharray="4 156"
        stroke-dashoffset="160">
      <animate attributeName="stroke-dashoffset" from="160" to="0" dur="4s" repeatCount="indefinite" />
      <animate attributeName="stroke-width" :values="`${lineWidth - 8};${lineWidth + 8};${lineWidth - 8}`" dur="2s" repeatCount="indefinite" />
    </path>
  </g>
</template>