Sfoglia il codice sorgente

TrafficTimeSpace 绿/蓝波带视觉优化与独立随机速度:

  - 绿波虚线由 bottomLine 外侧迁移到 topLine 外侧;蓝波虚线保持 bottomLine 外侧,两条虚线互为镜像
  - 绿波速度文字改到虚线同侧(midTop 外推),蓝波保持 midBottom 外推;文字角度统一用底线方向 atan2,去掉蓝波 -π
  翻转,修复 {bandwidth}s 时间标签反着读的问题
  - 绿波速度文字角度额外 +π/2(顺时针 90°),蓝波速度文字角度 -π/2(逆时针 90°)
  - 新增 greenSpeed / blueSpeed,组件初始化时各取 42~48km/h 的随机值(保留一位小数),getWaveData 用各自速度计算
  travelTime,速度标签渲染改用 item.speed
画安 1 settimana fa
parent
commit
cb2c870d7b
1 ha cambiato i file con 44 aggiunte e 21 eliminazioni
  1. 44 21
      src/components/ui/TrafficTimeSpace.vue

+ 44 - 21
src/components/ui/TrafficTimeSpace.vue

@@ -45,6 +45,9 @@ export default {
       scanLineTimer: null,
       currentViewMinX: 0,
       currentScanX: 0,
+      // 每条波带的速度 42~48 km/h 随机(组件初始化时生成一次)
+      greenSpeed: Math.round((42 + Math.random() * 6) * 10) / 10,
+      blueSpeed: Math.round((42 + Math.random() * 6) * 10) / 10,
     };
   },
   computed: {
@@ -134,21 +137,21 @@ export default {
     },
 
     getWaveData() {
-      const speedMs = this.speedKmh / 3.6; // 换算为 m/s
       const startX = this.intersections[0].x;
       const endX = this.intersections[this.intersections.length - 1].x;
-      const travelTime = (endX - startX) / speedMs;
+      const greenTravelTime = (endX - startX) / (this.greenSpeed / 3.6);
+      const blueTravelTime = (endX - startX) / (this.blueSpeed / 3.6);
 
       // 【终极微调】:发车时间精确控制
       // 正向绿波 (A -> D):10秒起步,刚好完美贴合所有绿灯边缘
       const gStartY = 10;
       const greenBottomLine = [
         [startX, gStartY],
-        [endX, gStartY + travelTime]
+        [endX, gStartY + greenTravelTime]
       ];
       const greenTopLine = [
         [startX, gStartY + this.bandwidth],
-        [endX, gStartY + travelTime + this.bandwidth]
+        [endX, gStartY + greenTravelTime + this.bandwidth]
       ];
       const greenCoords = [...greenBottomLine, ...[...greenTopLine].reverse()];
 
@@ -156,11 +159,11 @@ export default {
       const bStartYAtD = 100;
       const blueBottomLine = [
         [endX, bStartYAtD],
-        [startX, bStartYAtD + travelTime]
+        [startX, bStartYAtD + blueTravelTime]
       ];
       const blueTopLine = [
         [endX, bStartYAtD + this.bandwidth],
-        [startX, bStartYAtD + travelTime + this.bandwidth]
+        [startX, bStartYAtD + blueTravelTime + this.bandwidth]
       ];
       const blueCoords = [...blueBottomLine, ...[...blueTopLine].reverse()];
 
@@ -171,7 +174,8 @@ export default {
           topLine: greenTopLine,
           color: this.upWaveColor,
           lineCol: '#2ecc71',
-          isBlue: false
+          isBlue: false,
+          speed: this.greenSpeed
         },
         {
           coords: blueCoords,
@@ -179,7 +183,8 @@ export default {
           topLine: blueTopLine,
           color: this.downWaveColor,
           lineCol: '#3498db',
-          isBlue: true
+          isBlue: true,
+          speed: this.blueSpeed
         }
       ];
     },
@@ -246,10 +251,10 @@ export default {
               const pT1 = mappedTop[segIdx];
               const pT2 = mappedTop[segIdx + 1];
 
-              let angle = Math.atan2(pB2[1] - pB1[1], pB2[0] - pB1[0]);
-              if (item.isBlue) {
-                angle -= Math.PI;
-              }
+              // 波带角度统一用底线方向(atan2),保证文字沿着波带方向读
+              const angle = Math.atan2(pB2[1] - pB1[1], pB2[0] - pB1[0]);
+              // 绿波速度文字沿波带方向顺时针 90°;蓝波沿波带方向逆时针 90°
+              const speedAngle = item.isBlue ? angle - Math.PI / 2 : angle + Math.PI / 2;
 
               const percent = 0.15;
               const midBottomX = pB1[0] + (pB2[0] - pB1[0]) * percent;
@@ -257,24 +262,33 @@ export default {
               const midTopX = pT1[0] + (pT2[0] - pT1[0]) * percent;
               const midTopY = pT1[1] + (pT2[1] - pT1[1]) * percent;
 
-              // 速度文字沿底线垂直方向、远离波带方向偏移
+              // 速度文字沿波带法线向外偏移;与虚线保持同侧(绿波→顶线侧,蓝波→底线侧)
               const sBDx = pB2[0] - pB1[0];
               const sBDy = pB2[1] - pB1[1];
               const sBLen = Math.sqrt(sBDx * sBDx + sBDy * sBDy) || 1;
               const sPerpX = sBDy / sBLen;
               const sPerpY = -sBDx / sBLen;
               const sDot = (pT1[0] - pB1[0]) * sPerpX + (pT1[1] - pB1[1]) * sPerpY;
-              const sSign = sDot > 0 ? -1 : 1;
               const speedOff = this.fs(14);
-              const speedX = midBottomX + sSign * sPerpX * speedOff;
-              const speedY = midBottomY + sSign * sPerpY * speedOff;
+              let speedX, speedY;
+              if (item.isBlue) {
+                // 蓝波:底线外侧
+                const sSign = sDot > 0 ? -1 : 1;
+                speedX = midBottomX + sSign * sPerpX * speedOff;
+                speedY = midBottomY + sSign * sPerpY * speedOff;
+              } else {
+                // 绿波:顶线外侧(虚线这边)
+                const sSign = sDot > 0 ? 1 : -1;
+                speedX = midTopX + sSign * sPerpX * speedOff;
+                speedY = midTopY + sSign * sPerpY * speedOff;
+              }
 
               return {
                 type: 'group',
                 children: [
                   { type: 'polygon', shape: { points: mappedCoords }, style: { fill: item.color } },
                   ...(() => {
-                    // 虚线与绿波带之间留间隙,沿垂直于底线方向向外偏移
+                    // 虚线沿垂直于波带方向向外偏移:绿波走顶线外侧,蓝波走底线外侧(互相镜像)
                     const lineGap = this.fs(4);
                     const bDx = mappedBottom[1][0] - mappedBottom[0][0];
                     const bDy = mappedBottom[1][1] - mappedBottom[0][1];
@@ -286,13 +300,22 @@ export default {
                     const midTx = (mappedTop[0][0] + mappedTop[1][0]) / 2;
                     const midTy = (mappedTop[0][1] + mappedTop[1][1]) / 2;
                     const dot = (midTx - midBx) * perpX + (midTy - midBy) * perpY;
-                    const sign = dot > 0 ? -1 : 1;
-                    const offsetBottom = mappedBottom.map(p => [p[0] + sign * perpX * lineGap, p[1] + sign * perpY * lineGap]);
+
+                    let points;
+                    if (item.isBlue) {
+                      // 蓝波:底线外侧(远离顶线)
+                      const sign = dot > 0 ? -1 : 1;
+                      points = mappedBottom.map(p => [p[0] + sign * perpX * lineGap, p[1] + sign * perpY * lineGap]);
+                    } else {
+                      // 绿波:顶线外侧(远离底线)
+                      const sign = dot > 0 ? 1 : -1;
+                      points = mappedTop.map(p => [p[0] + sign * perpX * lineGap, p[1] + sign * perpY * lineGap]);
+                    }
                     return [
-                      { type: 'polyline', shape: { points: offsetBottom }, style: { stroke: item.lineCol, lineDash: [4, 4], lineWidth: 1 } }
+                      { type: 'polyline', shape: { points }, style: { stroke: item.lineCol, lineDash: [4, 4], lineWidth: 1 } }
                     ];
                   })(),
-                  { type: 'text', x: speedX, y: speedY, rotation: angle, style: { text: `${this.speedKmh}km/h`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center' } },
+                  { type: 'text', x: speedX, y: speedY, rotation: speedAngle, style: { text: `${item.speed}km/h`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center' } },
                   ...(() => {
                     const cxBw = (midBottomX + midTopX) / 2;
                     const cyBw = (midBottomY + midTopY) / 2;