在 Day 21 中,我們添加了即時尺寸監控功能來解決佈局問題。然而,在實際使用過程中,我們發現了更多深層的容器尺寸問題,特別是「觀看全地圖」功能和節點置中功能。今天,我們將深入探討這些問題,並提供完整的解決方案。
當使用者點擊「觀看全地圖」按鈕時,發現地圖變得非常小,幾乎看不見。透過瀏覽器開發者工具檢查,發現 zoomInner
容器的尺寸被設定為:
<div id="zoomInner" style="width: 4632px; height: 2763px; transform: translate(...)">
這個問題的根源是雙重縮放:
zoomInner
被設定為原始 SVG 尺寸(4632x2763px)transform: scale()
進行縮放這導致了以下問題:
scale()
縮放,讓圖片變得過小我們移除了 zoomInner
的固定尺寸設定:
// 修改前:設定固定尺寸
const finalWidth = Math.min(origW, maxReasonableWidth);
const finalHeight = Math.min(origH, maxReasonableHeight);
zoomInner.style.width = finalWidth + "px";
zoomInner.style.height = finalHeight + "px";
// 修改後:使用自動尺寸
zoomInner.style.width = "auto";
zoomInner.style.height = "auto";
在修復縮放問題後,我們發現背景圖片被拖曳和縮放,這不是預期的行為。背景圖片應該保持固定位置。
背景圖片原本設定在 #zoomInner::before
偽元素中,這會導致:
將背景圖片移動到 #graph
容器層級:
#graph {
background-image: url("世界地圖背景圖片");
background-repeat: no-repeat;
background-position: center 60%; /* 向下移動,避免被 header 擋住 */
background-size: cover;
background-attachment: fixed; /* 固定背景,不受拖曳影響 */
}
並添加淡化效果:
background-image:
linear-gradient(rgba(250, 248, 243, 0.85), rgba(250, 248, 243, 0.85)),
url("背景圖片");
在實作搜尋功能時,我們希望使用者點擊搜尋結果後,該節點能自動置中在螢幕中央。然而,置中功能完全失效,節點要麼跑到螢幕外,要麼位置不正確。
這是一個複雜的多層座標系統問題:
getBBox()
返回相對於 SVG 原點的座標transform
為了診斷問題,我們添加了詳細的 log 系統:
function centerNode(nodeId, targetScale = 150) {
console.log('=== 開始置中節點 ===');
console.log('目標節點 ID:', nodeId);
console.log('目標縮放比例:', targetScale);
// 找到指定節點
const nodeDetail = state.nodeDetails.find(n => n.id === nodeId);
if (!nodeDetail) {
console.log('❌ 找不到節點詳細資訊:', nodeId);
console.log('可用的節點:', state.nodeDetails.map(n => n.id));
return false;
}
console.log('✅ 找到節點詳細資訊:', nodeDetail);
// ... 更多詳細 log
}
在診斷過程中,我們發現 applyDragTransform
函數中的邊界限制過於嚴格:
// 問題:邊界限制太嚴格
const maxTranslateX = Math.max(0, (scaledWidth - containerWidth) / 2 + containerWidth * 0.5);
const minTranslateX = Math.min(0, -(scaledWidth - containerWidth) / 2 - containerWidth * 0.5);
// 解決:放寬邊界限制
const maxTranslateX = Math.max(0, (scaledWidth - containerWidth) / 2 + containerWidth * 2);
const minTranslateX = Math.min(0, -(scaledWidth - containerWidth) / 2 - containerWidth * 2);
正確的座標轉換邏輯:
// 1. 取得節點在 SVG 中的位置
const bbox = targetNodeElement.getBBox();
const nodeX = bbox.x + bbox.width / 2;
const nodeY = bbox.y + bbox.height / 2;
// 2. 計算縮放後的位置
const scale = state.scalePct / 100;
const scaledNodeX = nodeX * scale;
const scaledNodeY = nodeY * scale;
// 3. 計算需要移動的距離
const moveX = containerCenterX - scaledNodeX;
const moveY = containerCenterY - scaledNodeY;
// 4. 應用位移
state.currentTranslateX = moveX;
state.currentTranslateY = moveY;
在 Debug 模式中,我們發現每秒都會輸出大量的容器尺寸資訊,污染控制台。
移除詳細的 console.log 輸出,只保留必要的資訊更新:
// 修改前:大量 console.log 輸出
console.log('=== 容器尺寸詳細資訊 ===');
console.log('clientWidth:', graph.clientWidth);
console.log('clientHeight:', graph.clientHeight);
// ... 更多 log
// 修改後:移除詳細 log
// 移除詳細的 console.log 輸出,避免每秒輸出大量資訊
為了確保置中功能正常工作,我們建立了驗證系統:
// 驗證最終位置
setTimeout(() => {
const finalBbox = targetNodeElement.getBBox();
const finalNodeX = finalBbox.x + finalBbox.width / 2;
const finalNodeY = finalBbox.y + finalBbox.height / 2;
const finalScale = state.scalePct / 100;
// 計算節點在螢幕上的實際位置
const finalScreenX = (finalNodeX * finalScale) + state.currentTranslateX;
const finalScreenY = (finalNodeY * finalScale) + state.currentTranslateY;
const finalContainerCenterX = graph.clientWidth / 2;
const finalContainerCenterY = graph.clientHeight / 2;
console.log('=== 置中後驗證 ===');
console.log('節點在螢幕上的實際位置:', finalScreenX, finalScreenY);
console.log('容器中心:', finalContainerCenterX, finalContainerCenterY);
console.log('偏差:', Math.abs(finalScreenX - finalContainerCenterX), Math.abs(finalScreenY - finalContainerCenterY));
}, 100);
transform: scale()
requestAnimationFrame
優化動畫今天的開發過程讓我們深刻理解了容器尺寸管理的複雜性。從雙重縮放問題到精確的節點置中,每一個問題都需要仔細的診斷和系統性的解決方案。
透過建立完善的診斷工具和驗證系統,我們不僅解決了當前的問題,也為未來的開發奠定了堅實的基礎。這種系統性的問題解決方法,正是專業開發者必備的技能。
在 AI 時代,我們需要的不只是快速解決問題,更要建立可維護、可擴展的系統架構。今天的經驗,將成為我們在 Google Apps Script 開發路上的寶貴財富。