|
@@ -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 response = await fetch(MapSvg);
|
|
|
const svgText = await response.text();
|
|
@@ -114,13 +139,150 @@ const initSvg = async () => {
|
|
|
const svgElement = document.querySelector(".map-container svg");
|
|
|
if (svgElement) {
|
|
|
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,
|
|
|
fit: true,
|
|
|
center: true,
|
|
|
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();
|
|
|
}
|
|
|
};
|
|
|
|