設定 HTML 畫布元素的大小

最後更新: 2024-08-12 15:47:28 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

HTML 畫布有兩種大小:

  1. width 和 height 屬性定義其繪圖緩衝區。
  2. CSS 寬度和高度定義其顯示方框。

混淆這兩種大小會產生模糊的輸出、拉伸的座標,或過度的記憶體使用量。

繪圖緩衝區與顯示大小 #

畫布屬性包含不帶單位的像素計數:

html
<canvas width="800" height="400"></canvas>

預設的繪圖緩衝區為 300 x 150 像素。CSS 可以以其他大小顯示該緩衝區:

css
canvas {
  inline-size: 100%;
  block-size: auto;
}

當 CSS 方框與繪圖緩衝區的縱橫比不同時,瀏覽器會拉伸點陣圖。撰寫 width="800px" 在意圖上是無效的,因為該屬性是整數,而不是 CSS 長度。

變更 canvas.width 或 canvas.height 會清除點陣圖並重設繪圖內容狀態,包括轉換、裁剪區域、樣式和目前路徑。

符合裝置像素比例 #

高密度顯示器會將一個 CSS 像素映射至多個裝置像素。根據呈現的 CSS 方框設定後端儲存區大小:

js
function resizeCanvas(canvas, context) {
  const rect = canvas.getBoundingClientRect();
  if (rect.width === 0 || rect.height === 0) {
    return false;
  }

  const dpr = Math.min(window.devicePixelRatio || 1, 2);
  const width = Math.round(rect.width * dpr);
  const height = Math.round(rect.height * dpr);

  if (canvas.width !== width || canvas.height !== height) {
    canvas.width = width;
    canvas.height = height;
  }

  const scaleX = width / rect.width;
  const scaleY = height / rect.height;
  context.setTransform(scaleX, 0, 0, scaleY, 0, 0);
  return true;
}

套用轉換後,繪圖程式碼可以使用 CSS 像素座標,而後端儲存區仍保留高密度細節。當大型畫布會耗用過多記憶體時,請限制縮放比例:RGBA 緩衝區在計入實作特定的額外負荷前,每個像素約需四個位元組。

使用 setTransform,而不是重複呼叫 scale,來為每次重繪建立轉換。否則,重複乘上目前的轉換會使畫布在每次調整大小通知時持續放大。指定任一維度都會重設內容,即使指定的值等於目前值也是如此,因此請先進行比較,並在每次實際指定後重繪。

回應版面配置變更 #

畫布大小可能因為視窗大小調整、容器查詢、字型載入或周圍內容而變更。請觀察元素,而不是只監聽視窗大小調整:

js
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");

const observer = new ResizeObserver(() => {
  if (resizeCanvas(canvas, context)) {
    render(context);
  }
});

observer.observe(canvas);

避免變更繪圖緩衝區屬性而改變 CSS 大小所造成的回饋迴圈。讓 CSS 獨立控制顯示方框。

ResizeObserverEntry.devicePixelContentBoxSize 可以直接以裝置像素提供內容方框,無需經過 CSS 像素四捨五入步驟。較舊引擎的支援並不普遍,因此正式環境程式碼需要退回使用 contentBoxSize 或 getBoundingClientRect()。

座標映射與輸入 #

指標座標會以視窗 CSS 像素傳入。當繪圖程式碼使用後端儲存區座標時,請將其映射至繪圖緩衝區:

js
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) * (canvas.width / rect.width);
const y = (event.clientY - rect.top) * (canvas.height / rect.height);

CSS 轉換可能使顯示幾何形狀比單純縮放更複雜。對於經過轉換的互動畫布,請反轉適用的 DOMMatrix,而不是使用寬度比例。如果繪圖使用上一節的 CSS 像素轉換,減去 rect.left 和 rect.top 後已會產生邏輯繪圖座標;再次套用後端儲存區比例會使輸入縮放兩次。

程式庫與離屏呈現 #

圖形程式庫可能會在調整呈現器大小時更新畫布屬性。例如,程式庫可以將繪圖緩衝區與 CSS 方框分開設定。直接指定屬性前,請先讀取程式庫的大小 API,因為外部指定可能會捨棄呈現器狀態。

OffscreenCanvas 具有繪圖緩衝區大小,但沒有 CSS 方框。主執行緒必須將所需的像素尺寸傳達給工作執行緒。傳輸影像或呈現的畫格時,不要假設工作執行緒可以檢查版面配置。執行 transferControlToOffscreen() 後,繪圖所有權會歸屬於離屏畫布;從原始 DOM 畫布要求另一個繪圖內容是無效的。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)