前端图片压缩工具

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>前端图片压缩工具</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { 
            line-height: 1.6;
            background: #f5f7fa;
            padding: 20px; 
        }
        .container { max-width: 800px; margin: 0 auto; }
        header { text-align: center; margin-bottom: 30px; }
        h1 { color: #2c3e50; margin-bottom: 10px; }
        .main-content { display: flex; flex-direction: column; gap: 20px; }
        
        .upload-area {
            border: 2px dashed #3498db; border-radius: 10px; padding: 30px;
            text-align: center; cursor: pointer; background: white;
        }
        .upload-area:hover { border-color: #2980b9; background: #f8fafc; }
        .upload-icon { font-size: 40px; color: #3498db; margin-bottom: 10px; }
        
        .controls { display: flex; flex-direction: column; gap: 15px; background: white; padding: 20px; border-radius: 10px; }
        .control-group { display: flex; flex-direction: column; gap: 5px; }
        .control-group label { font-weight: 600; color: #2c3e50; }
        input[type="range"] { width: 100%; height: 6px; }
        
        .preview-container { display: flex; flex-wrap: wrap; gap: 20px; }
        .preview-box { flex: 1; min-width: 300px; background: white; padding: 20px; border-radius: 10px; }
        .image-box { border: 1px solid #ddd; border-radius: 8px; min-height: 200px; display: flex; align-items: center; justify-content: center; }
        .image-box img { max-width: 100%; max-height: 200px; }
        .image-info { display: flex; justify-content: space-between; margin-top: 10px; }
        
        .btn-group { display: flex; gap: 10px; margin-top: 20px; }
        .btn { flex: 1; padding: 12px; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; }
        .btn-primary { background: #3498db; color: white; }
        .btn-success { background: #2ecc71; color: white; }
        .btn:disabled { background: #bdc3c7; cursor: not-allowed; }
        
        .status { padding: 10px; border-radius: 6px; text-align: center; margin-top: 10px; display: none; }
        .status.success { background: #d4edda; color: #155724; }
        .status.error { background: #f8d7da; color: #721c24; }
        
        footer { text-align: center; margin-top: 30px; color: #7f8c8d; }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>前端图片压缩工具</h1>
            <p>基于Blob的纯前端图片处理方案</p>
        </header>
        
        <div class="main-content">
            <div class="upload-area" id="dropArea">
                <div class="upload-icon">📁</div>
                <p>点击或拖拽上传图片</p>
                <input type="file" id="fileInput" accept="image/*" hidden>
            </div>
            
            <div class="controls">
                <div class="control-group">
                    <label>压缩质量: <span id="qualityValue">80%</span></label>
                    <input type="range" id="quality" min="10" max="100" value="80">
                </div>
                <div class="control-group">
                    <label>最大宽度: <span id="widthValue">800px</span></label>
                    <input type="range" id="maxWidth" min="200" max="2000" value="800" step="100">
                </div>
            </div>
            
            <div class="preview-container">
                <div class="preview-box">
                    <h3>原图</h3>
                    <div class="image-box">
                        <img id="originalPreview" style="display:none;">
                        <p id="originalPlaceholder">等待上传图片</p>
                    </div>
                    <div class="image-info">
                        <span>尺寸: <span id="originalDimensions">-</span></span>
                        <span>大小: <span id="originalSize">-</span></span>
                    </div>
                </div>
                <div class="preview-box">
                    <h3>压缩后</h3>
                    <div class="image-box">
                        <img id="compressedPreview" style="display:none;">
                        <p id="compressedPlaceholder">等待压缩</p>
                    </div>
                    <div class="image-info">
                        <span>尺寸: <span id="compressedDimensions">-</span></span>
                        <span>大小: <span id="compressedSize">-</span></span>
                        <span>压缩: <span id="compressionRatio">-</span></span>
                    </div>
                </div>
            </div>
            
            <div class="btn-group">
                <button id="compressBtn" class="btn btn-primary" disabled>开始压缩</button>
                <button id="downloadBtn" class="btn btn-success" disabled>下载图片</button>
            </div>
            
            <div id="statusMessage" class="status"></div>
        </div>
        
        <footer>
            <p>纯前端图片压缩方案</p>
        </footer>
    </div>
    
    <script>
        // DOM元素
        const fileInput = document.getElementById('fileInput');
        const dropArea = document.getElementById('dropArea');
        const compressBtn = document.getElementById('compressBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const qualitySlider = document.getElementById('quality');
        const maxWidthSlider = document.getElementById('maxWidth');
        const qualityValue = document.getElementById('qualityValue');
        const widthValue = document.getElementById('widthValue');
        const originalPreview = document.getElementById('originalPreview');
        const compressedPreview = document.getElementById('compressedPreview');
        const originalPlaceholder = document.getElementById('originalPlaceholder');
        const compressedPlaceholder = document.getElementById('compressedPlaceholder');
        const originalDimensions = document.getElementById('originalDimensions');
        const originalSize = document.getElementById('originalSize');
        const compressedDimensions = document.getElementById('compressedDimensions');
        const compressedSize = document.getElementById('compressedSize');
        const compressionRatio = document.getElementById('compressionRatio');
        const statusMessage = document.getElementById('statusMessage');
        
        // 状态变量
        let originalFile = null;
        let originalImageData = null;
        let compressedBlob = null;
        
        // 工具函数
        function formatFileSize(bytes) {
            if (!bytes) return '-';
            const units = ['B', 'KB', 'MB'];
            let size = bytes;
            let unitIndex = 0;
            while (size >= 1024 && unitIndex < units.length - 1) {
                size /= 1024;
                unitIndex++;
            }
            return `${size.toFixed(2)} ${units[unitIndex]}`;
        }
        
        function showStatus(message, isError = false) {
            statusMessage.textContent = message;
            statusMessage.className = `status ${isError ? 'error' : 'success'}`;
            statusMessage.style.display = 'block';
            setTimeout(() => { statusMessage.style.display = 'none'; }, 3000);
        }
        
        // 事件监听
        qualitySlider.addEventListener('input', () => qualityValue.textContent = `${qualitySlider.value}%`);
        maxWidthSlider.addEventListener('input', () => widthValue.textContent = `${maxWidthSlider.value}px`);
        
        dropArea.addEventListener('click', () => fileInput.click());
        dropArea.addEventListener('dragover', (e) => e.preventDefault());
        dropArea.addEventListener('drop', (e) => {
            e.preventDefault();
            if (e.dataTransfer.files.length) handleFileSelect(e.dataTransfer.files[0]);
        });
        fileInput.addEventListener('change', (e) => {
            if (e.target.files.length) handleFileSelect(e.target.files[0]);
        });
        
        // 处理文件选择
        function handleFileSelect(file) {
            if (!file.type.startsWith('image/')) {
                showStatus('请选择图片文件!', true);
                return;
            }
            
            originalFile = file;
            const reader = new FileReader();
            
            reader.onload = (e) => {
                // 显示原图
                originalPreview.src = e.target.result;
                originalPreview.style.display = 'block';
                originalPlaceholder.style.display = 'none';
                originalSize.textContent = formatFileSize(file.size);
                
                // 获取图片尺寸
                const img = new Image();
                img.onload = () => {
                    originalDimensions.textContent = `${img.width} × ${img.height}`;
                    originalImageData = { width: img.width, height: img.height, size: file.size };
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file);
            
            compressBtn.disabled = false;
            showStatus('图片已加载,可以开始压缩');
        }
        
        // 图片压缩函数
        function compressImage(file, quality, maxWidth) {
            return new Promise((resolve) => {
                const reader = new FileReader();
                reader.readAsDataURL(file);
                
                reader.onload = (e) => {
                    const img = new Image();
                    img.src = e.target.result;
                    
                    img.onload = () => {
                        const canvas = document.createElement('canvas');
                        const ctx = canvas.getContext('2d');
                        
                        // 计算缩放比例
                        let width = img.width;
                        let height = img.height;
                        if (width > maxWidth) {
                            height = (maxWidth / width) * height;
                            width = maxWidth;
                        }
                        
                        canvas.width = width;
                        canvas.height = height;
                        ctx.drawImage(img, 0, 0, width, height);
                        
                        // Canvas转Blob
                        canvas.toBlob(
                            (blob) => resolve({ blob, width, height }),
                            'image/jpeg',
                            quality / 100
                        );
                    };
                };
            });
        }
        
        // 压缩按钮事件
        compressBtn.addEventListener('click', async () => {
            if (!originalFile) return;
            
            compressBtn.disabled = true;
            compressBtn.textContent = '压缩中...';
            
            try {
                const quality = parseInt(qualitySlider.value);
                const maxWidth = parseInt(maxWidthSlider.value);
                
                const result = await compressImage(originalFile, quality, maxWidth);
                compressedBlob = result.blob;
                
                // 显示压缩图片
                compressedPreview.src = URL.createObjectURL(compressedBlob);
                compressedPreview.style.display = 'block';
                compressedPlaceholder.style.display = 'none';
                compressedDimensions.textContent = `${result.width} × ${result.height}`;
                compressedSize.textContent = formatFileSize(compressedBlob.size);
                
                const ratio = ((originalImageData.size - compressedBlob.size) / originalImageData.size * 100).toFixed(1);
                compressionRatio.textContent = `${ratio}%`;
                
                downloadBtn.disabled = false;
                showStatus(`压缩完成!节省了 ${ratio}% 空间`);
            } catch (error) {
                showStatus('压缩失败,请重试', true);
            } finally {
                compressBtn.disabled = false;
                compressBtn.textContent = '开始压缩';
            }
        });
        
        // 下载按钮事件
        downloadBtn.addEventListener('click', () => {
            if (!compressedBlob) return;
            
            const url = URL.createObjectURL(compressedBlob);
            const link = document.createElement('a');
            link.href = url;
            link.download = `compressed-${originalFile.name.replace(/\.[^/.]+$/, "")}.jpg`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            
            setTimeout(() => URL.revokeObjectURL(url), 100);
            showStatus('开始下载压缩图片');
        });
        
        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            showStatus('上传图片开始压缩');
        });
    </script>
</body>
</html>

效果图如下
在这里插入图片描述
本文转载至@程序员大华,原文地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值