Browse Source

feat: restore after no operation on svg

chenlei 16 hours ago
parent
commit
19dd747ba7
2 changed files with 165 additions and 6 deletions
  1. 0 3
      packages/pc/src/views/Map/index.scss
  2. 165 3
      packages/pc/src/views/Map/index.vue

+ 0 - 3
packages/pc/src/views/Map/index.scss

@@ -17,9 +17,6 @@
     background: url("@/assets/images/title-min.png") no-repeat center / contain;
     background: url("@/assets/images/title-min.png") no-repeat center / contain;
   }
   }
   &-container {
   &-container {
-    display: flex;
-    align-items: center;
-    justify-content: center;
     width: 100%;
     width: 100%;
     height: 100%;
     height: 100%;
 
 

+ 165 - 3
packages/pc/src/views/Map/index.vue

@@ -104,6 +104,31 @@ const openDetail = (item) => {
   }
   }
 };
 };
 
 
+const AUTO_RESET_DELAY = 5000; // 5秒无操作自动重置
+let panZoom;
+let resetTimer;
+let countdownInterval;
+let remainingTime = AUTO_RESET_DELAY / 1000;
+
+const updateCountdown = () => {
+  remainingTime--;
+  if (remainingTime <= 0) {
+    clearInterval(countdownInterval);
+  }
+};
+
+const resetInactivityTimer = () => {
+  clearTimeout(resetTimer);
+  clearInterval(countdownInterval);
+
+  remainingTime = AUTO_RESET_DELAY / 1000;
+  countdownInterval = setInterval(updateCountdown, 1000);
+  resetTimer = setTimeout(() => {
+    panZoom.reset();
+    clearInterval(countdownInterval);
+  }, AUTO_RESET_DELAY);
+};
+
 const initSvg = async () => {
 const initSvg = async () => {
   const response = await fetch(MapSvg);
   const response = await fetch(MapSvg);
   const svgText = await response.text();
   const svgText = await response.text();
@@ -114,13 +139,150 @@ const initSvg = async () => {
   const svgElement = document.querySelector(".map-container svg");
   const svgElement = document.querySelector(".map-container svg");
   if (svgElement) {
   if (svgElement) {
     svgElement.id = "map-svg";
     svgElement.id = "map-svg";
-    svgPanZoom("#map-svg", {
+    const container = document.getElementById(svgElement.id);
+    const containerWidth = container.clientWidth;
+    const containerHeight = container.clientHeight;
+    const svgWidth = 558;
+    const svgHeight = 631;
+
+    function calculateBounds(zoom) {
+      const visibleWidth = containerWidth / zoom;
+      const visibleHeight = containerHeight / zoom;
+
+      // 计算边界:当缩小(zoom降低)时,边界范围会扩大
+      // 边界始终以SVG中心为对称点
+
+      // SVG中心坐标
+      const centerX = svgWidth / 2;
+      const centerY = svgHeight / 2;
+
+      // 可视区域的一半
+      const halfVisibleWidth = visibleWidth / 2;
+      const halfVisibleHeight = visibleHeight / 2;
+
+      // 计算边界值
+      let minX, maxX, minY, maxY;
+
+      // 水平方向边界
+      if (visibleWidth < svgWidth) {
+        // 可视区域小于SVG宽度:可以左右平移查看全部内容
+        minX = -(centerX - halfVisibleWidth); // 左边界
+        maxX = svgWidth - centerX - halfVisibleWidth; // 右边界
+      } else {
+        // 可视区域大于SVG宽度:完全显示SVG,边界范围扩大
+        // 此时边界是SVG外部区域,保持居中
+        const overflow = (visibleWidth - svgWidth) / 2;
+        minX = -centerX - overflow - svgWidth / 2; // 左边界(扩展到SVG外部)
+        maxX = svgWidth - centerX + overflow - svgWidth / 2; // 右边界(扩展到SVG外部)
+      }
+
+      // 垂直方向边界
+      if (visibleHeight < svgHeight) {
+        // 可视区域小于SVG高度:可以上下平移查看全部内容
+        minY = -(centerY - halfVisibleHeight + svgHeight); // 上边界
+        maxY = svgHeight - centerY - halfVisibleHeight; // 下边界
+      } else {
+        // 可视区域大于SVG高度:完全显示SVG,边界范围扩大
+        const overflow = (visibleHeight - svgHeight) / 2;
+        minY = -centerY - overflow; // 上边界(扩展到SVG外部)
+        maxY = svgHeight - centerY + overflow; // 下边界(扩展到SVG外部)
+      }
+
+      return { minX, maxX, minY, maxY };
+    }
+
+    function correctPositionAfterZoom() {
+      const zoom = panZoom.getZoom();
+      const currentPan = panZoom.getPan();
+      const bounds = calculateBounds(zoom);
+
+      // 修正X坐标
+      let correctedX = currentPan.x;
+      if (correctedX < bounds.minX) correctedX = bounds.minX;
+      if (correctedX > bounds.maxX) correctedX = bounds.maxX;
+
+      // 修正Y坐标
+      let correctedY = currentPan.y;
+      if (correctedY < bounds.minY) correctedY = bounds.minY;
+      if (correctedY > bounds.maxY) correctedY = bounds.maxY;
+
+      // 应用修正后的位置
+      if (correctedX !== currentPan.x || correctedY !== currentPan.y) {
+        panZoom.pan({ x: correctedX, y: correctedY });
+      }
+    }
+
+    panZoom = svgPanZoom("#map-svg", {
       zoomEnabled: true,
       zoomEnabled: true,
       fit: true,
       fit: true,
       center: true,
       center: true,
       minZoom: 0.8,
       minZoom: 0.8,
-      maxZoom: 3,
-    }).zoom(1.2);
+      maxZoom: 2,
+      customEventsHandler: {
+        init: function (panZoomInstance) {
+          this.panZoom = panZoomInstance.instance;
+          this.isPanning = false;
+          this.startX = 0;
+          this.startY = 0;
+          this.currentPan = { x: 0, y: 0 };
+
+          // 鼠标事件
+          svgElement.addEventListener("mousedown", (e) => {
+            this.start(e);
+            resetInactivityTimer();
+          });
+          document.addEventListener("mousemove", (e) => {
+            this.move(e);
+            resetInactivityTimer();
+          });
+          document.addEventListener("mouseup", (e) => this.end(e));
+
+          // 鼠标滚轮缩放事件
+          svgElement.addEventListener("wheel", () => {
+            correctPositionAfterZoom();
+            resetInactivityTimer();
+          });
+
+          return true;
+        },
+        start: function (event) {
+          if (event.button !== 0) return;
+          this.isPanning = true;
+          this.startX = event.clientX;
+          this.startY = event.clientY;
+          this.currentPan = this.panZoom.getPan();
+          event.preventDefault();
+        },
+        move: function (event) {
+          if (!this.isPanning) return;
+
+          const dx = event.clientX - this.startX;
+          const dy = event.clientY - this.startY;
+          const zoom = this.panZoom.getZoom();
+
+          // 计算新位置
+          let newX = this.currentPan.x + dx / zoom;
+          let newY = this.currentPan.y + dy / zoom;
+
+          // 获取当前缩放级别的动态边界
+          const bounds = calculateBounds(zoom);
+
+          // 应用边界限制
+          newX = Math.max(bounds.minX, Math.min(newX, bounds.maxX));
+          newY = Math.max(bounds.minY, Math.min(newY, bounds.maxY));
+
+          // 应用平移
+          this.panZoom.pan({ x: newX, y: newY });
+        },
+        end: function () {
+          this.isPanning = false;
+        },
+      },
+    });
+    panZoom.zoom(1.2);
+
+    // 初始化计时器
+    resetInactivityTimer();
   }
   }
 };
 };