使用 HTML5 Canvas 实现圆形图片裁剪并上传

前言

在Web开发中,有时候我们需要将用户上传的图片进行裁剪,特别是裁剪成圆形的头像图片。这篇博客将介绍如何使用HTML5 Canvas实现图片的圆形裁剪,并将裁剪后的图片上传到服务器。我们将详细讲解相关的代码实现过程,并提供一个完整的示例代码。

步骤概览

  1. 创建HTML结构,包含文件上传控件、Canvas和显示裁剪后图片的标签。
  2. 在 uploadFile.onchange 中创建一个 Image 对象,并在 onload 事件中获取图片的实际尺寸(image.width 和 image.height),我们可以计算出图片在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,我们可以灵活地处理图像,实现各种有趣的功能。

posted @ 2024-05-24 00:52  雪旭  阅读(30)  评论(0编辑  收藏  举报