iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0

Day 24: 我失敗了。試了半天之後,我失敗了。

前言:一個看似簡單的需求

今天我想要實現一個功能:當使用者搜尋特定節點後,自動將該節點置中到畫面中央。聽起來很簡單,對吧?就像 Google Maps 那樣,點擊一個地點就能自動移動到那裡。

但這個看似簡單的功能,卻讓我陷入了座標系統的地獄...

座標系統的複雜性

問題的根源

我發現了幾個不同的座標系統在同時運作:

  1. SVG 內部座標系統:Graphviz 生成的節點座標
  2. CSS Transform 座標系統zoomInnertranslatescale
  3. 螢幕像素座標系統:瀏覽器視窗的實際像素
  4. 容器座標系統#graph 容器的尺寸和位置

座標轉換的噩夢

// 我嘗試的各種計算方式
const nodeX = bbox.x + bbox.width / 2;  // SVG 座標
const nodeY = bbox.y + bbox.height / 2;

// 嘗試轉換到螢幕座標
const scaledX = nodeX * scale;
const scaledY = nodeY * scale;

// 嘗試計算移動距離
const moveX = containerCenterX - scaledX;
const moveY = containerCenterY - scaledY;

但這些計算總是會有微妙的偏移,讓我懷疑人生。

嘗試的各種方案

方案一:直接座標計算

function centerNode(nodeId) {
  const bbox = node.getBBox();
  const moveX = containerCenterX - nodeX;
  const moveY = containerCenterY - nodeY;
  // 結果:總是偏右邊
}

方案二:動態偵測節點位置

function centerNode(nodeId) {
  const rect = node.getBoundingClientRect();
  const containerRect = graph.getBoundingClientRect();
  // 結果:座標完全錯亂
}

方案三:先縮放後置中

function centerNode(nodeId) {
  // 先設定縮放為 100%
  state.scalePct = 100;
  applyZoom(100);
  // 然後再置中
  // 結果:還是有偏移
}

方案四:完全移除縮放功能

最後我決定移除所有縮放功能,固定使用 100% 縮放,但問題依然存在。

座標系統的差異

SVG 座標系統

  • Y 軸向下為正
  • 單位可能是 pt、px 或其他
  • 原點在左上角

CSS Transform 座標系統

  • Y 軸向下為正
  • 單位是 px
  • 原點在元素中心

螢幕座標系統

  • Y 軸向下為正
  • 單位是 px
  • 原點在視窗左上角

這些差異讓座標轉換變得極其複雜。

最終的妥協方案

經過無數次的嘗試,我決定採用一個更實用的方案:讓使用者直接拖曳 SVG 來移動地圖

// 拖曳 SVG 本身
document.addEventListener('mousedown', function(e) {
  if (e.target.closest('svg')) {
    startDrag(e);
  }
});

// 避免在節點上啟動拖曳
if (e.target.closest('g.node')) {
  return; // 不啟動拖曳
}

這個方案雖然不是自動置中,但至少讓使用者能夠手動將想要的節點移到畫面中央。

技術收穫

1. 座標系統的複雜性

我深刻體會到不同座標系統之間的轉換是多麼複雜。每個系統都有自己的原點、單位和方向。

2. 縮放對座標的影響

縮放比例會影響所有座標計算,即使設定為 100%,仍然可能有微妙的差異。

3. 瀏覽器渲染的差異

不同瀏覽器對 SVG 和 CSS Transform 的處理可能有細微差異。

4. 事件處理的複雜性

在 SVG 上處理滑鼠事件需要考慮事件冒泡、座標轉換等問題。

程式碼的演進

最初的置中功能

function centerNode(nodeId) {
  const bbox = node.getBBox();
  const moveX = containerCenterX - bbox.x;
  const moveY = containerCenterY - bbox.y;
  zoomInner.style.transform = `translate(${moveX}px, ${moveY}px)`;
}

加入縮放考慮

function centerNode(nodeId) {
  const scale = state.scalePct / 100;
  const scaledX = bbox.x * scale;
  const scaledY = bbox.y * scale;
  // 複雜的計算...
}

最終的拖曳方案

// 讓使用者直接拖曳 SVG
document.addEventListener('mousedown', function(e) {
  if (e.target.closest('svg') && !e.target.closest('g.node')) {
    startDrag(e);
  }
});

反思與學習

為什麼會失敗?

  1. 過度複雜化:我試圖用程式碼解決一個可以用使用者操作解決的問題
  2. 座標系統理解不足:沒有充分理解不同座標系統之間的關係
  3. 完美主義:追求完美的置中,但忽略了實用性

學到的教訓

  1. 有時候手動操作比自動化更好:讓使用者直接拖曳可能比複雜的自動置中更直觀
  2. 座標轉換是程式設計的難題:這不是我的問題,而是這個領域的普遍難題
  3. 失敗也是學習:每一次失敗都讓我更了解這些技術的複雜性

未來的改進方向

短期改進

  1. 優化拖曳體驗,加入平滑動畫
  2. 添加拖曳邊界限制
  3. 改善觸控設備的支援

長期目標

  1. 深入研究 SVG 座標系統
  2. 學習更進階的座標轉換技術
  3. 考慮使用現成的地圖庫(如 Leaflet、OpenLayers)

結語:失敗也是一種收穫

今天的開發確實是「徒勞無功」的,我沒有實現自動置中功能。但是,這個過程讓我深刻理解了:

  • 座標系統的複雜性
  • 不同技術棧之間的整合難題
  • 程式設計中「簡單」與「複雜」的界限

有時候,承認某個功能太複雜,改用更簡單的方案,也是一種智慧。今天的「失敗」讓我學會了:

  1. 技術選擇的重要性:不是所有功能都值得用複雜的程式碼實現
  2. 使用者體驗的優先級:手動拖曳可能比自動置中更直觀
  3. 失敗的價值:每一次失敗都是學習的機會

雖然今天沒有做出成果,但這個嘗試又嘗試的過程,本身就是一種收穫。在程式設計的路上,失敗和成功一樣重要,甚至更重要,因為失敗教會我們更多。

明天,我會帶著今天的經驗,繼續前進。也許下次遇到類似的問題時,我會更早地選擇更簡單的解決方案,而不是陷入座標系統的地獄中。

沒有做出成果,也是一種成果。什麼收穫都沒有,也是一種收穫。


Day 24 完成。座標系統的地獄,讓我學會了謙遜,也讓我學會了選擇。


上一篇
Day 23: 介面美學革命 - 從現代化到古樸風格的視覺轉型
系列文
亨利羊帶你 Google Apps Script 從入門到精通:放棄長篇大論的教學吧,你需要的只是一些精心設計的 prompt!24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言