在 HTML Canvas 上使用使用者上傳的 PNG 檔案

 好的,如果你想在 HTML Canvas 上使用使用者上傳的 PNG 檔案,主要涉及以下幾個步驟:

  1. HTML 部分:提供檔案上傳控制項
    你需要一個 <input type="file"> 元素讓使用者選擇檔案。

  2. JavaScript 部分:

    • 監聽檔案選擇事件:當使用者選擇了檔案後,<input type="file"> 元素的 change 事件會被觸發。

    • 讀取檔案內容:使用 FileReader API 來讀取上傳的檔案內容。對於圖片,通常會將其讀取為 Data URL。

    • 創建 Image 物件:創建一個新的 HTMLImageElement (new Image())。

    • 設定 Image 物件的 src:將 FileReader 讀取到的 Data URL 設定為 Image 物件的 src 屬性。

    • 監聽 Image 物件的 load 事件:圖片載入是異步的。你需要等待圖片完全載入後才能在 Canvas 上繪製它。

    • load 事件回呼中繪製圖片:一旦圖片載入完成,就可以使用 ctx.drawImage() 將其繪製到 Canvas 上。

以下是一個完整的範例程式碼:

      <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload PNG to Canvas</title>
    <style>
        body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; margin-top: 20px; }
        canvas { border: 1px solid black; margin-top: 20px; }
        input[type="file"] { margin-bottom: 10px; }
    </style>
</head>
<body>

    <label for="imageUpload">選擇一個 PNG 檔案:</label>
    <input type="file" id="imageUpload" accept="image/png">

    <canvas id="myCanvas" width="400" height="300"></canvas>

    <script>
        const imageUpload = document.getElementById('imageUpload');
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');

        imageUpload.addEventListener('change', function(event) {
            const files = event.target.files; // 獲取選擇的檔案列表

            if (files && files.length > 0) {
                const file = files[0]; // 我們只處理第一個選擇的檔案

                // 檢查檔案類型是否為 PNG (可選,但推薦)
                if (file.type === "image/png") {
                    const reader = new FileReader();

                    // 當 FileReader 成功讀取檔案後觸發
                    reader.onload = function(e) {
                        const img = new Image(); // 創建一個新的 Image 物件

                        // 當 Image 物件成功載入圖片數據後觸發
                        img.onload = function() {
                            // 清除之前的畫布內容 (可選)
                            ctx.clearRect(0, 0, canvas.width, canvas.height);

                            // 將圖片繪製到 Canvas 上
                            // ctx.drawImage(image, dx, dy);
                            // ctx.drawImage(image, dx, dy, dWidth, dHeight);
                            // ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

                            // 範例1:將圖片繪製在 (0,0) 位置,使用圖片原始尺寸
                            // ctx.drawImage(img, 0, 0);

                            // 範例2:將圖片縮放/拉伸以適應 Canvas 的一部分或全部
                            // 例如,將圖片繪製在 (10,10),寬度200,高度150
                            // ctx.drawImage(img, 10, 10, 200, 150);

                            // 範例3:保持圖片的原始長寬比,使其適應 Canvas (較複雜,但常用)
                            const canvasWidth = canvas.width;
                            const canvasHeight = canvas.height;
                            const imgWidth = img.naturalWidth; // 圖片的原始寬度
                            const imgHeight = img.naturalHeight; // 圖片的原始高度

                            const hRatio = canvasWidth / imgWidth;
                            const vRatio = canvasHeight / imgHeight;
                            const ratio = Math.min(hRatio, vRatio); // 取較小的縮放比例以確保圖片完整顯示

                            const newWidth = imgWidth * ratio;
                            const newHeight = imgHeight * ratio;

                            // 計算繪製位置,使圖片在 Canvas 中居中
                            const offsetX = (canvasWidth - newWidth) / 2;
                            const offsetY = (canvasHeight - newHeight) / 2;

                            ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);

                            console.log(`圖片 "${file.name}" 已繪製到 Canvas。`);
                        };

                        // 當 Image 物件載入圖片數據失敗時觸發
                        img.onerror = function() {
                            console.error("載入圖片時發生錯誤。");
                            alert("無法載入圖片。請確保檔案格式正確。");
                        };

                        // 設定 Image 物件的 src 為 FileReader 讀取到的 Data URL
                        // Data URL 是一個以 "data:" 開頭的 URL,它直接包含了檔案的內容
                        img.src = e.target.result;
                    };

                    // 當 FileReader 讀取檔案失敗時觸發
                    reader.onerror = function() {
                        console.error("讀取檔案時發生錯誤。");
                        alert("讀取檔案失敗。");
                    };

                    // 開始讀取檔案,將其讀取為 Data URL
                    reader.readAsDataURL(file);

                } else {
                    alert("請選擇一個 PNG 格式的圖片檔案。");
                    // 清空檔案選擇,以便使用者可以重新選擇
                    event.target.value = null;
                }
            }
        });
    </script>

</body>
</html>
    

程式碼解釋:

  1. imageUpload.addEventListener('change', ...):

    • 當使用者通過檔案選擇框選擇了一個或多個檔案後,這個事件會被觸發。

    • event.target.files 是一個 FileList 物件,包含了使用者選擇的所有檔案。我們通常取第一個 files[0]

  2. 檔案類型檢查 (if (file.type === "image/png")):

    • 這是一個前端的簡單檢查,可以提示使用者選擇正確的檔案類型。但這不是絕對安全的,伺服器端仍然需要進行驗證(如果檔案會上傳到伺服器的話)。

  3. FileReader:

    • const reader = new FileReader();:創建一個 FileReader 實例。

    • reader.onload = function(e) { ... }:當 FileReader 成功完成讀取操作時的回呼函數。e.target.result 將會包含讀取到的檔案內容(在這裡是 Data URL)。

    • reader.onerror = function() { ... }:讀取失敗時的回呼。

    • reader.readAsDataURL(file);:這是觸發 FileReader 開始讀取檔案的方法。它會將檔案內容編碼成一個 Base64 的 Data URL 字串。

  4. Image 物件:

    • const img = new Image();:創建一個 HTMLImageElement。這和你在 HTML 中寫 <img src="..."> 是類似的,只是我們在 JavaScript 中動態創建和操作它。

    • img.onload = function() { ... }:這是非常關鍵的一步。圖片載入是異步的。你必須等到圖片的數據完全下載並解碼完成後,才能在 Canvas 上繪製它。這個回呼函數會在圖片準備好時執行。

    • img.onerror = function() { ... }:圖片載入失敗時的回呼。

    • img.src = e.target.result;:將 FileReader 生成的 Data URL 賦值給 img.src。這會觸發瀏覽器開始載入圖片數據。

  5. ctx.drawImage(image, ...):

    • img.onload 回呼中,圖片已經準備好了。

    • ctx.drawImage() 有多種重載形式:

      • ctx.drawImage(image, dx, dy): 在 Canvas 的 (dx, dy) 位置繪製圖片,使用圖片的原始尺寸。

      • ctx.drawImage(image, dx, dy, dWidth, dHeight): 在 Canvas 的 (dx, dy) 位置繪製圖片,並將其縮放/拉伸到指定的 dWidthdHeight

      • ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight): (切片繪製) 從源圖片的 (sx, sy) 位置開始,截取 sWidth x sHeight 大小的區域,然後將這個區域繪製到 Canvas 的 (dx, dy) 位置,並縮放/拉伸到 dWidth x dHeight

    • 範例中,我提供了一個保持圖片長寬比並使其在 Canvas 中居中顯示的常用方法。img.naturalWidthimg.naturalHeight 用於獲取圖片的原始像素尺寸。

流程總結:
選擇檔案 -> FileReader 讀取為 Data URL -> Image 物件的 src 設為 Data URL -> Image 物件觸發 load 事件 -> 在 load 回呼中使用 ctx.drawImage() 繪製。

這個流程可以確保你總是在圖片完全可用時才嘗試繪製它,避免了常見的錯誤。

留言

這個網誌中的熱門文章

Offscreen Canvas

外部網頁新增 Google 日曆行程(URL模板)

計算 Canvas 文字最大字體