使用 HTML5 Canvas 實現圓形圖片裁剪並上傳

雪旭發表於2024-05-24

前言

在Web開發中,有時候我們需要將使用者上傳的圖片進行裁剪,特別是裁剪成圓形的頭像圖片。這篇部落格將介紹如何使用HTML5 Canvas實現圖片的圓形裁剪,並將裁剪後的圖片上傳到伺服器。我們將詳細講解相關的程式碼實現過程,並提供一個完整的示例程式碼。

步驟概覽

  1. 建立HTML結構,包含檔案上傳控制元件、Canvas和顯示裁剪後圖片的標籤。
  2. 使用FileReader讀取使用者上傳的圖片,並在Canvas上繪製圖片。
  3. 將Canvas的繪製區域裁剪為圓形。
  4. 將裁剪後的圖片轉換為Blob物件,並顯示在頁面上。
  5. 將Blob物件上傳到伺服器。

HTML結構

首先,我們建立一個簡單的HTML結構,包括一個檔案上傳控制元件、一個隱藏的Canvas用於繪製和裁剪圖片,以及一個用於顯示裁剪後圖片的img標籤。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas {
            display: none;
            border: 1px solid red;
        }

        .upload {
            width: 100px;
            height: 20px;
            border: 1px solid;
            position: relative;
        }

        .upload-file {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            z-index: 1;
            cursor: pointer;
        }

        .upload-btn {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            text-align: center;
            line-height: 1.5;
            font-size: 14px;
            color: #666;
            border-radius: 5px;
        }
    </style>
</head>

<body>
    <div class="upload">
        <input type="file" class="upload-file">
        <div class="upload-btn">圖片上傳</div>
    </div>
    <div class="upload-content">
        <img src="" class="upload-img">
    </div>
    <canvas id="canvas"></canvas>
    <script>

    </script>
</body>

</html>

JavaScript實現

在JavaScript中,我們將實現以下幾個關鍵步驟:

1. 讀取並繪製圖片

使用FileReader讀取使用者上傳的圖片,並在Canvas上繪製該圖片。這裡確保Canvas的寬高比與圖片一致,以避免圖片變形。

const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = 300;
        canvas.height = 300;
        const uploadFile = document.querySelector('.upload-file');
        const uploadImg = document.querySelector('.upload-img');
        let imageBlob;

        uploadFile.onchange = (e) => {
            const fileData = e.target.files[0];
            const reader = new FileReader();
            const image = new Image();

            reader.readAsDataURL(fileData); // 非同步讀取檔案內容,結果用data:url的字串形式表示
            reader.onload = function (e) {
                image.src = this.result;
                image.onload = function () {
                    // 清空畫布
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    // 計算圖片繪製區域,確保圖片保持比例
                    const aspectRatio = image.width / image.height;
                    let drawWidth, drawHeight;
                    let offsetX = 0, offsetY = 0;

                    if (aspectRatio > 1) {
                        // 圖片更寬
                        drawHeight = canvas.height;
                        drawWidth = drawHeight * aspectRatio;
                        offsetX = (canvas.width - drawWidth) / 2;
                    } else {
                        // 圖片更高
                        drawWidth = canvas.width;
                        drawHeight = drawWidth / aspectRatio;
                        offsetY = (canvas.height - drawHeight) / 2;
                    }

                    // 將畫布剪裁為圓形區域
                    ctx.beginPath();
                    ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, Math.PI * 2);
                    ctx.closePath();
                    ctx.clip();

                    // // 在剪裁後的圓形區域內繪製圖片
                    ctx.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);

                    // 將畫布內容轉換為圖片
                    canvas.toBlob(function (blob) {
                        const url = URL.createObjectURL(blob);
                        uploadImg.src = url;
                        imageBlob = blob;
                    }, 'image/png');
                };
            }
        }

2. 上傳圖片

實現將裁剪後的圖片上傳到伺服器。這裡我們使用FormData物件將Blob物件封裝,並透過Fetch API將其上傳到伺服器。

 uploadBtn.addEventListener('click', () => {
            if (imageBlob) {
                const formData = new FormData();
                formData.append('image', imageBlob, 'image.png');

                fetch('/upload', {
                    method: 'POST',
                    body: formData,
                })
                    .then(response => response.json())
                    .then(data => {
                        console.log('Success:', data);
                    })
                    .catch((error) => {
                        console.error('Error:', error);
                    });
            } else {
                console.error('No image available to upload.');
            }
});

完整示例

將上述HTML和JavaScript程式碼結合起來,我們得到了一個完整的實現使用者上傳圖片、裁剪為圓形並上傳的示例。以下是完整的程式碼:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        canvas {
            display: none;
            border: 1px solid red;
        }

        .upload {
            width: 100px;
            height: 20px;
            border: 1px solid;
            position: relative;
        }

        .upload-file {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            opacity: 0;
            z-index: 1;
            cursor: pointer;
        }

        .upload-btn {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            text-align: center;
            line-height: 1.5;
            font-size: 14px;
            color: #666;
            border-radius: 5px;
        }
    </style>
</head>

<body>
    <div class="upload">
        <input type="file" class="upload-file">
        <div class="upload-btn">圖片上傳</div>
    </div>
    <div class="upload-content">
        <img src="" class="upload-img">
    </div>
    <canvas id="canvas"></canvas>
    <button id="uploadBtn">Upload Image</button>
    <script>
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = 300;
        canvas.height = 300;
        const uploadFile = document.querySelector('.upload-file');
        const uploadImg = document.querySelector('.upload-img');
        let imageBlob;

        uploadFile.onchange = (e) => {
            const fileData = e.target.files[0];
            const reader = new FileReader();
            const image = new Image();

            reader.readAsDataURL(fileData); // 非同步讀取檔案內容,結果用data:url的字串形式表示
            reader.onload = function (e) {
                image.src = this.result;
                image.onload = function () {
                    // 清空畫布
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    // 計算圖片繪製區域,確保圖片保持比例
                    const aspectRatio = image.width / image.height;
                    let drawWidth, drawHeight;
                    let offsetX = 0, offsetY = 0;

                    if (aspectRatio > 1) {
                        // 圖片更寬
                        drawHeight = canvas.height;
                        drawWidth = drawHeight * aspectRatio;
                        offsetX = (canvas.width - drawWidth) / 2;
                    } else {
                        // 圖片更高
                        drawWidth = canvas.width;
                        drawHeight = drawWidth / aspectRatio;
                        offsetY = (canvas.height - drawHeight) / 2;
                    }

                    // 將畫布剪裁為圓形區域
                    ctx.beginPath();
                    ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, Math.PI * 2);
                    ctx.closePath();
                    ctx.clip();

                    // // 在剪裁後的圓形區域內繪製圖片
                    ctx.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);

                    // 將畫布內容轉換為圖片
                    canvas.toBlob(function (blob) {
                        const url = URL.createObjectURL(blob);
                        uploadImg.src = url;
                        imageBlob = blob;
                    }, 'image/png');
                };
            }
        }

        uploadBtn.addEventListener('click', () => {
            if (imageBlob) {
                const formData = new FormData();
                formData.append('image', imageBlob, 'image.png');

                fetch('/upload', {
                    method: 'POST',
                    body: formData,
                })
                    .then(response => response.json())
                    .then(data => {
                        console.log('Success:', data);
                    })
                    .catch((error) => {
                        console.error('Error:', error);
                    });
            } else {
                console.error('No image available to upload.');
            }
        });
    </script>
</body>

</html>

結論

透過上述步驟,我們實現了一個簡單的Web應用程式,能夠讓使用者上傳圖片,並將圖片裁剪為圓形後顯示在頁面上,並提供上傳到伺服器的功能。使用HTML5 Canvas和JavaScript,我們可以靈活地處理影像,實現各種有趣的功能。

相關文章