preloader
心得

SVG vs Canvas vs touch-action Comparison of Zoom and Coordinate System | SVG vs Canvas vs touch-action 縮放與座標系比較

SVG vs Canvas vs touch-action Comparison of Zoom and Coordinate System | SVG vs Canvas vs touch-action 縮放與座標系比較

座標系統

SVG — 聲明式座標系,內建視窗變換

<!-- viewBox 定義世界座標,瀏覽器自動映射到元素尺寸 -->
<svg width="400" height="300" viewBox="0 0 1000 750">
  <circle cx="500" cy="375" r="50" />  <!-- 世界座標 -->
</svg>

SVG 的 viewBox 就是一個內建的座標變換:

  • viewBox="0 0 1000 750" = 世界座標範圍
  • width="400" height="300" = 螢幕尺寸
  • 瀏覽器自動計算縮放比:400/1000 = 0.4

Canvas — 命令式座標系,手動管理

// 沒有 viewBox,必須自己算
ctx.scale(scale, scale);
ctx.translate(offsetX, offsetY);
ctx.beginPath();
ctx.arc(500, 375, 50, 0, Math.PI * 2); // 世界座標
ctx.fill();

對比

SVG Canvas
座標映射 viewBox 自動處理 手動 ctx.scale/translate
重繪 瀏覽器自動(改 attribute 即可) 必須清除 + 完整重繪
表達方式 聲明式(DOM 節點) 命令式(繪圖指令)

縮放方式

SVG 有三種縮放方法

<!-- 方法 1:改 viewBox(最自然,類似 Canvas 的世界座標變換) -->
<svg viewBox="250 187 500 375">  <!-- 放大 2 倍,看中心區域 -->

<!-- 方法 2:用 <g> 的 transform(類似 CSS transform) -->
<svg>
  <g transform="scale(2) translate(-125, -93)">
    <circle cx="500" cy="375" r="50" />
  </g>
</svg>

<!-- 方法 3:CSS transform(和 img 一樣,拉伸像素) -->
<svg style="transform: scale(2);">
方法 畫質 重排版 適用場景
改 viewBox 清晰(向量重算) 地圖、資料視覺化
<g> transform 清晰(SVG 內部變換) 部分 局部放大
CSS transform 可能模糊(光柵化後縮放) 簡單動畫

Canvas 只有一種方法

ctx.clearRect(0, 0, w, h);
ctx.save();
ctx.scale(scale, scale);
ctx.translate(offsetX, offsetY);
drawEverything(ctx);  // 必須重繪
ctx.restore();

關鍵差異:SVG 改 viewBox 後,瀏覽器用向量重新算每個圖形,永遠清晰。Canvas 也是重繪所以也清晰。但 CSS transform 作用在 SVG 上時,瀏覽器可能先光柵化再縮放,就會模糊。


點擊偵測

SVG — 瀏覽器自動處理

<!-- SVG 元素就是 DOM 節點,事件直接綁 -->
<circle cx="500" cy="375" r="50" (click)="onClick($event)" />

即使套了 viewBox 或 <g> transform,瀏覽器自動換算命中區域。

Canvas — 必須手動反算

canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const screenX = e.clientX - rect.left;
  const screenY = e.clientY - rect.top;
  // 手動反算世界座標
  const worldX = (screenX - offsetX) / scale;
  const worldY = (screenY - offsetY) / scale;
  // 再逐一判斷是否命中某個圖形
  shapes.forEach(shape => {
    if (isInsideShape(worldX, worldY, shape)) { /* ... */ }
  });
});
SVG Canvas
點擊偵測 瀏覽器自動,支援事件冒泡 完全手動,自己算座標 + 命中判定
座標轉換 自動處理 viewBox/transform 手動 (screen - offset) / scale
hover 效果 CSS :hover 直接用 自己追蹤滑鼠位置 + 重繪

搭配 touch-action

SVG Canvas HTML 可捲動內容
最佳縮放方式 改 viewBox ctx.scale + 重繪 transform + margin
平移方式 改 viewBox 的 x, y ctx.translate + 重繪 原生捲動
touch-action none(JS 全權處理) none(JS 全權處理) pan-x pan-y(捲動交給瀏覽器)
HammerJS 負責 pinch + pan pinch + pan 只負責 pinch

SVG 和 Canvas 都用 touch-action: none,因為平移都是 JS 自己算的(改 viewBox 或 ctx.translate),不需要瀏覽器原生捲動。


選擇建議

場景 選擇 原因
圖形數量少、需要互動 SVG 事件處理免費,向量永遠清晰
圖形數量多(>1000) Canvas DOM 節點太多 SVG 會卡
靜態圖表 + 縮放 SVG viewBox 最簡單
即時繪圖/遊戲 Canvas 命令式重繪更可控
長文件/報表 HTML + scroll 策略 需要原生捲動能力
單張圖片 HTML + transform 策略 最簡單,GPU 加速