温州市万年长

基于 HTML5 Canvas 实现图片旋转与下载功能(代码展示)

2026-03-30 14:52:01 浏览次数:0
详细信息
HTML5 Canvas图片旋转与下载功能实现

我将创建一个完整、美观的图片旋转与下载工具,使用纯HTML5 Canvas技术实现。

设计思路

下面是完整的实现代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas图片旋转与下载工具</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 30px 20px;
            color: #333;
        }

        .container {
            width: 100%;
            max-width: 900px;
            background-color: white;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            overflow: hidden;
            padding: 25px;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding-bottom: 20px;
            border-bottom: 1px solid #eee;
        }

        h1 {
            color: #2c3e50;
            font-size: 2.5rem;
            margin-bottom: 10px;
        }

        .subtitle {
            color: #7f8c8d;
            font-size: 1.1rem;
        }

        .app-content {
            display: flex;
            flex-wrap: wrap;
            gap: 30px;
        }

        .canvas-section {
            flex: 1;
            min-width: 300px;
        }

        .canvas-container {
            border: 2px dashed #bdc3c7;
            border-radius: 10px;
            background-color: #f8f9fa;
            width: 100%;
            height: 400px;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            margin-bottom: 20px;
        }

        canvas {
            max-width: 100%;
            max-height: 100%;
            display: block;
        }

        .controls-section {
            flex: 1;
            min-width: 300px;
        }

        .control-group {
            background-color: #f8f9fa;
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
        }

        h2 {
            color: #2c3e50;
            font-size: 1.5rem;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #eaeaea;
        }

        .upload-btn {
            display: block;
            width: 100%;
            padding: 15px;
            background-color: #3498db;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1.1rem;
            cursor: pointer;
            transition: background-color 0.3s;
            text-align: center;
            margin-bottom: 15px;
        }

        .upload-btn:hover {
            background-color: #2980b9;
        }

        #fileInput {
            display: none;
        }

        .rotation-controls {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 15px;
        }

        .rotation-btn {
            flex: 1;
            min-width: 120px;
            padding: 12px;
            background-color: #2ecc71;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.3s;
            text-align: center;
        }

        .rotation-btn:hover {
            background-color: #27ae60;
        }

        .rotation-btn.reset {
            background-color: #e74c3c;
        }

        .rotation-btn.reset:hover {
            background-color: #c0392b;
        }

        .angle-input-group {
            display: flex;
            align-items: center;
            margin-top: 15px;
        }

        .angle-input-group label {
            margin-right: 10px;
            font-weight: 600;
            color: #2c3e50;
        }

        .angle-input-group input {
            flex: 1;
            padding: 10px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 1rem;
        }

        .angle-input-group button {
            margin-left: 10px;
            padding: 10px 20px;
            background-color: #9b59b6;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        .angle-input-group button:hover {
            background-color: #8e44ad;
        }

        .download-btn {
            display: block;
            width: 100%;
            padding: 16px;
            background-color: #e67e22;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1.2rem;
            font-weight: 600;
            cursor: pointer;
            transition: background-color 0.3s;
            text-align: center;
            margin-top: 10px;
        }

        .download-btn:hover {
            background-color: #d35400;
        }

        .info {
            margin-top: 25px;
            padding: 15px;
            background-color: #f9f9f9;
            border-left: 4px solid #3498db;
            border-radius: 5px;
        }

        .info h3 {
            color: #2c3e50;
            margin-bottom: 8px;
        }

        .info p {
            color: #555;
            line-height: 1.5;
        }

        footer {
            margin-top: 30px;
            text-align: center;
            color: #7f8c8d;
            font-size: 0.9rem;
        }

        @media (max-width: 768px) {
            .app-content {
                flex-direction: column;
            }

            .canvas-container {
                height: 350px;
            }

            h1 {
                font-size: 2rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>Canvas图片旋转与下载工具</h1>
            <p class="subtitle">上传图片,旋转任意角度,然后下载处理后的图片</p>
        </header>

        <div class="app-content">
            <div class="canvas-section">
                <div class="canvas-container">
                    <canvas id="imageCanvas" width="600" height="400">
                        您的浏览器不支持Canvas,请升级浏览器。
                    </canvas>
                    <div id="placeholderText">请上传图片</div>
                </div>
                <div class="control-group">
                    <h2>上传图片</h2>
                    <button class="upload-btn" id="uploadTrigger">选择图片文件</button>
                    <input type="file" id="fileInput" accept="image/*">
                    <p style="text-align: center; color: #7f8c8d; margin-top: 10px;">支持 JPG, PNG, GIF 格式</p>
                </div>
            </div>

            <div class="controls-section">
                <div class="control-group">
                    <h2>旋转控制</h2>
                    <div class="rotation-controls">
                        <button class="rotation-btn" id="rotateLeft">左转 90°</button>
                        <button class="rotation-btn" id="rotateRight">右转 90°</button>
                        <button class="rotation-btn" id="rotate180">旋转 180°</button>
                        <button class="rotation-btn reset" id="resetRotation">重置角度</button>
                    </div>

                    <div class="angle-input-group">
                        <label for="angleInput">自定义角度:</label>
                        <input type="number" id="angleInput" min="0" max="360" value="0" step="1">
                        <button id="applyAngle">应用角度</button>
                    </div>

                    <div style="margin-top: 15px; text-align: center; font-weight: 600;">
                        当前旋转角度: <span id="currentAngle">0</span>°
                    </div>
                </div>

                <div class="control-group">
                    <h2>下载图片</h2>
                    <button class="download-btn" id="downloadBtn">下载旋转后的图片</button>
                    <p style="margin-top: 15px; color: #7f8c8d; text-align: center;">
                        点击下载按钮保存处理后的图片到您的设备
                    </p>
                </div>

                <div class="info">
                    <h3>使用说明</h3>
                    <p>1. 点击"选择图片文件"按钮上传图片</p>
                    <p>2. 使用旋转按钮或输入自定义角度旋转图片</p>
                    <p>3. 点击"下载旋转后的图片"保存处理结果</p>
                </div>
            </div>
        </div>

        <footer>
            <p>基于 HTML5 Canvas 实现的图片旋转与下载工具 | 纯前端实现,无需上传到服务器</p>
        </footer>
    </div>

    <script>
        // 获取DOM元素
        const canvas = document.getElementById('imageCanvas');
        const ctx = canvas.getContext('2d');
        const fileInput = document.getElementById('fileInput');
        const uploadTrigger = document.getElementById('uploadTrigger');
        const rotateLeftBtn = document.getElementById('rotateLeft');
        const rotateRightBtn = document.getElementById('rotateRight');
        const rotate180Btn = document.getElementById('rotate180');
        const resetRotationBtn = document.getElementById('resetRotation');
        const angleInput = document.getElementById('angleInput');
        const applyAngleBtn = document.getElementById('applyAngle');
        const downloadBtn = document.getElementById('downloadBtn');
        const currentAngleSpan = document.getElementById('currentAngle');
        const placeholderText = document.getElementById('placeholderText');

        // 状态变量
        let currentImage = null;
        let rotationAngle = 0; // 角度值
        let canvasWidth = 600;
        let canvasHeight = 400;

        // 初始化Canvas
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;

        // 上传图片
        uploadTrigger.addEventListener('click', () => {
            fileInput.click();
        });

        fileInput.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (!file) return;

            if (!file.type.match('image.*')) {
                alert('请选择图片文件');
                return;
            }

            const reader = new FileReader();
            reader.onload = (event) => {
                const img = new Image();
                img.onload = () => {
                    currentImage = img;
                    rotationAngle = 0;
                    currentAngleSpan.textContent = rotationAngle;
                    angleInput.value = rotationAngle;
                    placeholderText.style.display = 'none';
                    drawImage();
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(file);
        });

        // 绘制图片到Canvas
        function drawImage() {
            if (!currentImage) return;

            // 清除Canvas
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // 计算旋转后的Canvas尺寸
            const radians = rotationAngle * Math.PI / 180;

            // 计算旋转后的边界框
            const sin = Math.abs(Math.sin(radians));
            const cos = Math.abs(Math.cos(radians));

            // 原始图片尺寸
            const imgWidth = currentImage.width;
            const imgHeight = currentImage.height;

            // 计算旋转后的新尺寸
            const newWidth = imgWidth * cos + imgHeight * sin;
            const newHeight = imgWidth * sin + imgHeight * cos;

            // 调整Canvas大小以适应旋转后的图片
            canvas.width = newWidth;
            canvas.height = newHeight;

            // 保存Canvas状态
            ctx.save();

            // 移动Canvas原点至中心
            ctx.translate(newWidth / 2, newHeight / 2);

            // 应用旋转
            ctx.rotate(radians);

            // 绘制图片(居中)
            ctx.drawImage(
                currentImage, 
                -imgWidth / 2, 
                -imgHeight / 2, 
                imgWidth, 
                imgHeight
            );

            // 恢复Canvas状态
            ctx.restore();
        }

        // 旋转按钮事件
        rotateLeftBtn.addEventListener('click', () => {
            rotationAngle -= 90;
            normalizeAngle();
            updateAngleDisplay();
            drawImage();
        });

        rotateRightBtn.addEventListener('click', () => {
            rotationAngle += 90;
            normalizeAngle();
            updateAngleDisplay();
            drawImage();
        });

        rotate180Btn.addEventListener('click', () => {
            rotationAngle += 180;
            normalizeAngle();
            updateAngleDisplay();
            drawImage();
        });

        resetRotationBtn.addEventListener('click', () => {
            rotationAngle = 0;
            updateAngleDisplay();
            drawImage();
        });

        // 应用自定义角度
        applyAngleBtn.addEventListener('click', () => {
            const angle = parseInt(angleInput.value);
            if (!isNaN(angle)) {
                rotationAngle = angle;
                normalizeAngle();
                updateAngleDisplay();
                drawImage();
            }
        });

        // 确保角度在0-360度范围内
        function normalizeAngle() {
            rotationAngle = rotationAngle % 360;
            if (rotationAngle < 0) {
                rotationAngle += 360;
            }
        }

        // 更新角度显示
        function updateAngleDisplay() {
            currentAngleSpan.textContent = rotationAngle;
            angleInput.value = rotationAngle;
        }

        // 下载图片
        downloadBtn.addEventListener('click', () => {
            if (!currentImage) {
                alert('请先上传图片');
                return;
            }

            // 创建一个临时Canvas用于生成下载图片
            const tempCanvas = document.createElement('canvas');
            const tempCtx = tempCanvas.getContext('2d');

            // 计算旋转后的尺寸
            const radians = rotationAngle * Math.PI / 180;
            const sin = Math.abs(Math.sin(radians));
            const cos = Math.abs(Math.cos(radians));

            const imgWidth = currentImage.width;
            const imgHeight = currentImage.height;

            const newWidth = imgWidth * cos + imgHeight * sin;
            const newHeight = imgWidth * sin + imgHeight * cos;

            tempCanvas.width = newWidth;
            tempCanvas.height = newHeight;

            // 绘制旋转后的图片到临时Canvas
            tempCtx.translate(newWidth / 2, newHeight / 2);
            tempCtx.rotate(radians);
            tempCtx.drawImage(
                currentImage, 
                -imgWidth / 2, 
                -imgHeight / 2, 
                imgWidth, 
                imgHeight
            );

            // 生成下载链接
            const dataURL = tempCanvas.toDataURL('image/png');
            const link = document.createElement('a');
            link.href = dataURL;

            // 生成文件名
            const originalName = fileInput.files[0]?.name || 'image';
            const nameWithoutExt = originalName.substring(0, originalName.lastIndexOf('.'));
            const extension = originalName.substring(originalName.lastIndexOf('.'));

            link.download = `${nameWithoutExt}_rotated_${rotationAngle}deg${extension}`;

            // 触发下载
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        });

        // 初始化示例图片
        function loadExampleImage() {
            const img = new Image();
            img.onload = () => {
                currentImage = img;
                drawImage();
                placeholderText.style.display = 'none';
            };
            // 使用一个示例图片
            img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAwIiBoZWlnaHQ9IjQwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMzQ5OERCIi8+CiAgPHRleHQgeD0iNTAwIiB5PSIyMDAiIGZvbnQtc2l6ZT0iMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIiBmb250LWZhbWlseT0iQXJpYWwiPkNhbnZhcyBJbWFnZSBSb3RhdG9yPC90ZXh0PgogIDx0ZXh0IHg9IjUwMCIgeT0iMjQwIiBmb250LXNpemU9IjE2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSJ3aGl0ZSIgZm9udC1mYW1pbHk9IkFyaWFsIj5VcGxvYWQgeW91ciBvd24gaW1hZ2UgdG8gYmVnaW48L3RleHQ+Cjwvc3ZnPg==';
        }

        // 页面加载完成后加载示例图片
        window.addEventListener('DOMContentLoaded', loadExampleImage);
    </script>
</body>
</html>

功能说明

图片上传:点击"选择图片文件"按钮上传本地图片

旋转控制

下载功能:将旋转后的图片下载到本地设备

特点

该工具完全在浏览器中运行,图片处理不会上传到任何服务器,保护用户隐私。您可以直接复制上面的代码到HTML文件中,用浏览器打开即可使用。

相关推荐