Explorar el Código

地图标记点跟随缩放级别自适应大小

  - 新增zoomchange事件监听,缩放时实时更新标记点尺寸
  - 改用CSS变量(--dot-size, --special-size等)驱动所有标记点尺寸,替代逐个遍历更新DOM的方式
  - getDotSizeByZoom支持双向缩放:zoom>15放大、zoom<15缩小,范围6px-28px
  - 圆点文字在尺寸过小时自动隐藏(--text-display)
  - 异常/起终点标记使用--special-size联动缩放
  - 使用anchor属性替代手动计算offset,简化定位逻辑
  - 移除updateDotMarkerSizes和dotMarkers遍历逻辑,性能更优
画安 hace 1 semana
padre
commit
2b35a86ab4
Se han modificado 1 ficheros con 33 adiciones y 57 borrados
  1. 33 57
      src/components/TongzhouTrafficMap.vue

+ 33 - 57
src/components/TongzhouTrafficMap.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="map-wrapper">
+  <div class="map-wrapper" :style="mapCssVars">
     <div ref="mapContainer" class="map-container"></div>
 
     <div class="map-legend" :style="privateStyle.legend" v-if="(!mode || mode === '路口')"
@@ -83,7 +83,8 @@ export default {
         { name: "故障", color: "#FF3938", type: "abnormal" }
       ],
       intersectionData: [],
-      statusIntersections: {}
+      statusIntersections: {},
+      currentZoomSize: 14, // 保存当前的动态尺寸
     };
   },
   mounted() {
@@ -177,6 +178,18 @@ export default {
         return this.statusConfig.filter(item => !['干线协调', '勤务路线'].includes(item.name));
       }
       return [];
+    },
+    // 自动计算并下发给所有 CSS 的变量字典
+    mapCssVars() {
+      const size = this.currentZoomSize;
+      const specialSize = Math.max(16, size * 1.5);
+      return {
+        '--dot-size': `${size}px`,
+        '--dot-padding': size >= 14 ? '2px' : '0px',
+        '--text-display': size >= 14 ? 'flex' : 'none',
+        '--text-size': `${Math.max(10, size - 2)}px`,
+        '--special-size': `${specialSize}px`
+      };
     }
   },
   methods: {
@@ -299,6 +312,12 @@ export default {
             this.drawStaticRoutes();
           }
         });
+
+        this.map.on('zoomchange', () => {
+          if (!this.isComponentDestroyed) {
+            this.currentZoomSize = this.getDotSizeByZoom();
+          }
+        });
       } catch (err) {
         console.error('地图加载失败:', err);
       }
@@ -915,30 +934,9 @@ export default {
     getDotSizeByZoom() {
       if (!this.map) return 14;
       const zoom = this.map.getZoom();
-      const base = 14;
-      const extra = Math.max(0, zoom - 15) * 3;
-      return Math.min(base + extra, 28);
-    },
-
-    /**
-     * 遍历所有已注册的普通圆点 marker,更新其 content 和 offset
-     */
-    updateDotMarkerSizes() {
-      if (!this.isMapReady()) return;
-      const size = this.getDotSizeByZoom();
-      const half = Math.round(size / 2) + 2;
-      this.dotMarkers.forEach(({ marker, config, isRoute }) => {
-        if (!marker || typeof marker.setContent !== 'function') return;
-        const displayText = config.name ? config.name.charAt(0) : '';
-        const border = isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)';
-        const boxShadow = isRoute ? 'none' : `0 0 8px ${config.color}`;
-        marker.setContent(`
-          <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width:${size}px;height:${size}px;background:${config.color || '#999'};box-shadow:${boxShadow};border:${border};box-sizing:content-box;display:flex;justify-content:center;align-items:center;color:#fff;border-radius:50%;cursor:pointer;padding:2px;">
-            <span style="transform:scale(0.8);font-weight:bold;font-size:${Math.max(10, size - 2)}px;">${displayText}</span>
-          </div>
-        `);
-        marker.setOffset(new this.AMap.Pixel(-half, -half));
-      });
+      // 基准:zoom=15时是14px。每缩小一级减小3px。
+      const size = 14 + (zoom - 15) * 3;
+      return Math.min(Math.max(6, size), 28); // 最小值设为 6px
     },
 
     /**
@@ -965,30 +963,12 @@ export default {
         const isStartEnd = type === 'start' || type === 'end';
         const isPassed = type === 'passed';
 
-        const markerStyle = isStartEnd ? {
-          size: '24px',
-          height: '30px',
-          offset: [-12, -30],
-          zIndex: 120,
-          border: '2px solid #fff'
-        } : (isAbnormal ? {
-          size: '30px',
-          height: '30px',
-          offset: [-15, -15],
-          zIndex: 110,
-          border: 'none'
-        } : {
-          size: '18px',
-          height: '18px',
-          offset: [-11, -11],
-          zIndex: 100,
-          border: isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)'
-        });
-
         let markerContent = '';
+        
+        // --- 核心优化:尺寸全部使用 var(--xxx) CSS变量接管 ---
         if (isStartEnd) {
           markerContent = `
-            <div class="pure-light-node start-end-node" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: transparent; border: none; display: flex; justify-content: center; align-items: flex-end; cursor: pointer; transform-origin: bottom center;">
+            <div class="pure-light-node start-end-node" style="width: var(--special-size); height: calc(var(--special-size) + 6px); background: transparent; border: none; display: flex; justify-content: center; align-items: flex-end; cursor: pointer; transform-origin: bottom center;">
               <img src="${require(`@/assets/map/${type}.png`)}" style="width: 100%; height: auto; object-fit: contain; pointer-events: none;" />
             </div>
           `;
@@ -1001,23 +981,24 @@ export default {
         } else if (isAbnormal) {
           const iconName = config.name === '离线' ? 'lixian' : config.name === '降级' ? 'jiangji' : 'guzhang';
           markerContent = `
-            <div class="pure-light-node breathe" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: transparent; border: none; box-sizing: content-box; display: flex; justify-content: center; align-items: center; cursor: pointer; padding: 0;">
+            <div class="pure-light-node breathe" style="width: var(--special-size); height: var(--special-size); background: transparent; border: none; display: flex; justify-content: center; align-items: center; cursor: pointer; padding: 0;">
               <img src="${require(`@/assets/images/icon_${iconName}.png`)}" style="width: 100%; height: 100%; object-fit: contain;" />
             </div>
           `;
         } else {
           markerContent = `
-            <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width: ${markerStyle.size}; height: ${markerStyle.height}; background: ${config.color || '#999'}; box-shadow: ${isRoute ? 'none' : `0 0 8px ${config.color}`}; border: ${markerStyle.border}; box-sizing: content-box; display: flex; justify-content: center; align-items: center; color: #fff; border-radius: 50%; cursor: pointer; padding: 2px;">
-              <span style="transform: scale(0.8); font-weight: bold; font-size: 14px;">${displayText}</span>
+            <div class="pure-light-node ${isRoute ? 'route-node' : ''}" style="width: var(--dot-size); height: var(--dot-size); background: ${config.color || '#999'}; box-shadow: ${isRoute ? 'none' : `0 0 8px ${config.color}`}; border: ${isRoute ? 'none' : '1.5px solid rgba(255,255,255,0.7)'}; box-sizing: border-box; display: flex; justify-content: center; align-items: center; color: #fff; border-radius: 50%; cursor: pointer; padding: var(--dot-padding);">
+              <span style="display: var(--text-display); transform: scale(0.8); font-weight: bold; font-size: var(--text-size);">${displayText}</span>
             </div>
           `;
         }
 
         const marker = new this.AMap.Marker({
           position: [lng, lat],
-          zIndex: markerStyle.zIndex,
+          zIndex: isStartEnd ? 120 : (isAbnormal ? 110 : 100),
           content: markerContent,
-          offset: new this.AMap.Pixel(...markerStyle.offset),
+          // 核心优化:高德 2.0 原生支持 anchor 属性。代替手动计算 Offset!
+          anchor: isStartEnd ? 'bottom-center' : 'center', 
           extData: {
             ...config,
             position: [lng, lat],
@@ -1029,11 +1010,6 @@ export default {
           }
         });
 
-        if (!isStartEnd && !isAbnormal && !isPassed) {
-          const isRoute = ["干线协调", "勤务路线"].includes(config.name);
-          this.dotMarkers.push({ marker, config, isRoute });
-        }
-
         marker.on('click', (e) => {
           if (this.isComponentDestroyed) return;
           const extData = e.target.getExtData();