圖片來源:https://www.pixiv.net/artworks/66206141

【WHATWG】canvas 畫面長寬與像素長寬

雜談:

  我曾經有發表過一篇有關 canvas 畫面與像素長寬的 文章。文章中是藉由寫程式來實驗的方式去尋找 canvas 標籤對於內部像素以及外部畫面長寬的設定規則,最後再去 MDN 尋找相關文件證實這個規則。而在文末也有提及自己是先上網查詢、多方嘗試後,撞牆了一段時間才得出這個答案。

 

  事實上這種設定類的問題在規範中大多都有完整的定義存在。比起針對問題寫程式來實驗,查閱規範除了可以更快速獲得正確答案外,也能更清楚確定是各瀏覽器還是自己未正確實作規範。為了訓練自己查閱規範的能力,我開啟這個新系列記錄我以 WHATWG 為主、MDN 為輔的方式解答我在工作或個人專案上所遇到的 HTML 標籤問題。而這篇文章就是該系列的第一篇,決定使用規範解答我過去遇到的 canvas 問題。

 

問題:

  canvas HTML 元件本身是一個同時提供多種不同繪圖模式的像素畫布。使用者可以藉由各模式所對應的繪圖 API 輕易將 2d 圖像、3d 動畫等效果渲染在此像素畫布上。由於 canvas 本身內部就有一個像素畫布,所以不論 canvas 處於何種模式都會有兩個長寬可以被設定:

  • 準備接受繪圖 API 指令,被繪圖的內部像素畫布長寬。
  • 最終顯示在瀏覽器上所佔的位置長寬。

  如何分別設定這兩者就是這篇文章的重點。

 

規範解答:

  從 WHATWG 中的章節 4.12.5 The canvas element 可以得到這個答案。

 

The canvas element has two attributes to control the size of the element's bitmap: width and height. These attributes, when specified, must have values that are valid non-negative integers.

The rules for parsing non-negative integers must be used to obtain their numeric values. If an attribute is missing, or if parsing its value returns an error, then the default value must be used instead. The width attribute defaults to 300, and the height attribute defaults to 150.

  規範提到 canvas HTML 元件的 width 與 height 屬性就是用來控制內部 bitmap 像素畫布的長寬的,且預設長寬分別為 150 與 300。

 

A canvas element can be sized arbitrarily by a style sheet, its bitmap is then subject to the 'object-fit' CSS property.

  而 canvas HTML 元件本身的大小,也就是在瀏覽器上所佔的位置長寬,則可以被 CSS 所設定,同時當長寬值或比例不同時將依照 object-fit 這個 CSS 屬性設置。

 

  在章節 4.12.5.1 The 2D rendering context 中更可以看到 canvas 在 2d 模式下設定這兩種長寬的範例程式碼。

 

the canvas element below's rendering context has a 200x200 output bitmap (which internally uses CSS pixels as a unit for ease of integration with CSS) and is rendered as 100x100 CSS pixels:

<canvas width=200 height=200 style=width:100px;height:100px>

 

實際範例一:分別設定兩種長寬:

<html>
    <title>canvas width height size example</title>
    <style>
        table, td {
            border: 1px solid black;
        }
        img, canvas {
            width: 500px;
        }
    </style>
    <body>
        <table>
            <tr>
                <td><img id="source" src="887401.png"></td>
                <td><canvas width="1920" height="1080"></canvas></td>
            </tr>
            <tr>
                <td><canvas width="192" height="108"></canvas></td>
                <td><canvas width="48" height="27"></canvas></td>
            </tr>
        </table>
        <script>
            let image = document.getElementById('source');
            image.onload = () => {
                let canvasElements = document.getElementsByTagName('canvas');
                Array.from(canvasElements).forEach((canvasElement) => {
                    let canvasContext = canvasElement.getContext('2d');
                    canvasContext.drawImage(
                        image, 0, 0,
                        canvasElement.width,
                        canvasElement.height
                    );
                });
            };
        </script>
    </body>
</html>

  在範例一中我們創建了三個 CSS 長寬相同但內部畫布像素長寬不同的 canvas。並在每一張畫布上繪製一張原始尺寸為 1920 x 1080 的圖片。

 

  從瀏覽器顯示結果上可以看見,雖然這些 canvas 顯示的大小都是一樣的,但與左上角的原圖相比,下方的兩個 canvas 顯示的畫素卻較低。因為在於其內部畫布的像素本身就不高 (分別是 192 x 108 以及 48 x 27)。

 

實際範例二:分別設定 object-fit:

<html>
    <title>canvas object_fit size example</title>
    <style>
        table, td {
            border: 1px solid black;
        }
        img, canvas {
            width: 500px;
        }
        canvas {
            height: 200px;
        }
        #canvas01 {
            object-fit: fill;
        }
        #canvas02 {
            object-fit: contain;
        }
        #canvas03 {
            object-fit: cover;
        }
    </style>
    <body>
        <table>
            <tr>
                <td><img id="source" src="887401.png"></td>
                <td><canvas id="canvas01" width="1920" height="1080"></canvas></td>
            </tr>
            <tr>
                <td><canvas id="canvas02" width="1920" height="1080"></canvas></td>
                <td><canvas id="canvas03" width="1920" height="1080"></canvas></td>
            </tr>
        </table>
        <script>
            var image = document.getElementById('source');
            image.onload = () => {
                let canvasElements = document.getElementsByTagName('canvas');
                Array.from(canvasElements).forEach((canvasElement) => {
                    let canvasContext = canvasElement.getContext('2d');
                    canvasContext.drawImage(
                        image, 0, 0,
                        canvasElement.width,
                        canvasElement.height
                    );
                });
            };
        </script>
    </body>
</html>

  在範例二中我們創建了三個 CSS 長寬與內部畫布像素長寬比例不同且 object-fit 值不一樣的 canvas。同時在每一張畫布上繪製一張原始尺寸為 1920 x 1080 的圖片。

 

  這些 canvas 的畫布像素長寬都與原始圖片一樣為 1920 x 1080,可以完整呈現圖片的畫質,但由於其 CSS 長寬比例 (5 : 2) 與內部像素圖 (16 : 9) 不同,所以在顯示時勢必會做調整。從瀏覽器顯示結果上可以看見調整的方式即為 object-fit 的值 (fill、contain、cover)。

 

參考資料:

[1] WHATWG canvas

https://html.spec.whatwg.org/multipage/canvas.html

[2] 範例中使用的圖片

https://www.pixiv.net/artworks/66206141

 

文章標籤
全站熱搜
創作者介紹
創作者 迷宮兔 的頭像
迷宮兔

兔窩

迷宮兔 發表在 痞客邦 留言(0) 人氣(220)