使用 HTML、CSS 和 JavaScript 构建批量图像压缩工具
高分辨率图片虽然好看,但会明显拖慢页面加载速度,还会占用大量存储空间。
后端压缩工具很常见,但做一个纯客户端的图片压缩器有一个巨大优势:隐私。图片直接在浏览器里处理,用户数据完全不会经过服务器。
本教程将带你构建一个功能完整的浏览器端批量图片压缩工具。你将使用 HTML5 Canvas API 压缩图片体积,并结合 JSZip 库把多个压缩后的文件打包成一个 ZIP 方便下载。
为了让这个项目更实用,代码会被组织成一个可嵌入的组件。省略标准 HTML 骨架标签后,你可以直接把这段代码丢进 WordPress 的 Custom HTML 区块或任何其他 CMS 中,不会引起布局冲突。
前置知识
学习本教程前,你需要对以下内容有基本了解:
HTML & CSS:搭建 UI 结构,实现悬停和拖拽等交互状态。
JavaScript Promises:处理文件读取、ZIP 生成等异步操作。
Canvas API:了解浏览器如何原生绘制和操作图片数据。
目录
第一步:搭建 HTML 结构
首先创建可视化界面。你需要构建一个拖拽上传区、一个隐藏的文件输入框,以及一个面板——在用户选好图片后才会显示出来。
把下面的代码添加到你的文件中。注意开头就引入了 JSZip 的 CDN 链接,这样后面的脚本才能调用它。
<!-- JSZip 库,用于批量下载 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<div class="ic-main-wrapper">
<div class="ic-title">图片压缩工具</div>
<!-- 拖放区域 -->
<div class="ic-dropzone-area" id="icDropzone">
<input type="file" id="icFileInput" multiple accept="image/jpeg, image/png, image/webp, image/gif, image/bmp, image/tiff, image/avif" style="display: none;">
<div class="ic-icon-box">
<svg viewBox="0 0 24 24">
<path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zm-5-7l-3 3.72L9 13l-3 4h14l-4-5z"/>
</svg>
</div>
<div class="ic-primary-text">
将图片拖至此处,或<span class="ic-browse-link" id="icBrowseBtn">点击浏览</span>
</div>
<div class="ic-secondary-text">
支持多图——批量压缩并打包为 ZIP 下载
</div>
<div class="ic-format-tags">
<span>JPEG</span><span>PNG</span><span>WebP</span><span>GIF</span><span>BMP</span><span>TIFF</span><span>AVIF</span>
</div>
</div>
<!-- 操作面板(默认隐藏) -->
<div class="ic-action-panel" id="icActionPanel">
<div id="icStatusText" class="ic-status-msg">已选择 0 张图片</div>
<button class="ic-btn-primary" id="icCompressBtn">压缩并下载 ZIP</button>
</div>
</div>
理解 HTML 结构
accept="..."属性:限制隐藏的<input type="file">只接受图片格式,防止用户上传 PDF 或文本文档。可嵌入的结构:该标记依赖单一的
.ic-main-wrapper容器,而非完整的<html>和<body>标签,因此可以安全地嵌入现有网页,不会破坏父级主题。
第二步:使用 CSS 设计界面
接下来,应用样式让工具显得专业且支持响应式布局。采用简洁现代的设计风格,并以特定品牌强调色(#1A73E8)来突出可交互元素。
将下方 <style> 代码块放在 HTML 上方:
<style>
.ic-main-wrapper {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 850px;
margin: 20px auto;
background: #ffffff;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 20px rgba(0,0,0,0.04);
}
.ic-title {
font-size: 2.2rem;
font-weight: 700;
color: #0f172a;
text-align: center;
margin-bottom: 30px;
}
.ic-dropzone-area {
border: 1.5px dashed #cbd5e1;
border-radius: 12px;
padding: 60px 20px;
text-align: center;
background-color: #f8fafc;
transition: all 0.3s ease;
}
/* 拖拽激活状态 */
.ic-dropzone-area.dragover {
border-color: #1A73E8;
background-color: #f1f5f9;
}
.ic-icon-box {
width: 64px;
height: 64px;
background-color: #e8f0fe;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
color: #1A73E8;
}
.ic-icon-box svg {
width: 28px;
height: 28px;
fill: currentColor;
}
.ic-primary-text {
font-size: 1.15rem;
color: #0f172a;
font-weight: 500;
margin-bottom: 12px;
}
.ic-browse-link {
color: #1A73E8;
cursor: pointer;
text-decoration: none;
font-weight: 500;
}
.ic-browse-link:hover {
text-decoration: underline;
}
.ic-secondary-text {
font-size: 0.95rem;
color: #64748b;
margin-bottom: 24px;
}
.ic-format-tags {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.ic-format-tags span {
border: 1px solid #e2e8f0;
background: #ffffff;
color: #64748b;
font-size: 0.8rem;
padding: 6px 18px;
border-radius: 20px;
}
.ic-action-panel {
margin-top: 25px;
display: none;
text-align: center;
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border: 1px solid #e2e8f0;
}
.ic-btn-primary {
background: #1A73E8;
color: #ffffff;
border: none;
padding: 12px 28px;
border-radius: 6px;
font-size: 1rem;
cursor: pointer;
font-weight: 500;
transition: background 0.3s;
}
.ic-btn-primary:hover {
background: #1557b0;
}
.ic-btn-primary:disabled {
background: #94a3b8;
cursor: not-allowed;
}
.ic-status-msg {
margin-bottom: 15px;
font-size: 0.95rem;
color: #334155;
font-weight: 500;
}
</style>
CSS 代码解析:
.dragover 类:该类用于改变拖放区域的边框和背景颜色。你将通过 JavaScript 动态应用此类,当用户将文件悬停在区域上方时提供关键的视觉反馈。
唯一前缀:注意每个类都以
ic-(Image Compressor)开头。这充当了 CSS 命名空间,确保你的工具样式不会意外覆盖或被网站上的其他样式覆盖。
至此,用户界面已完成结构和样式搭建。效果如下所示:
第 3 步:添加 JavaScript 逻辑
接下来是工具的核心引擎。JavaScript 将处理文件选择,使用 Canvas API 压缩图片,并将其打包为 ZIP 文件。
在 HTML 下方添加此 <script> 代码块:
<script>
document.addEventListener('DOMContentLoaded', () => {
const dropzone = document.getElementById('icDropzone');
const fileInput = document.getElementById('icFileInput');
const browseBtn = document.getElementById('icBrowseBtn');
const actionPanel = document.getElementById('icActionPanel');
const statusText = document.getElementById('icStatusText');
const compressBtn = document.getElementById('icCompressBtn');
let filesArray = [];
// 1. 处理文件输入和拖放
browseBtn.addEventListener('click', (e) => {
e.stopPropagation();
fileInput.click();
});
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', () => {
dropzone.classList.remove('dragover');
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
if (e.dataTransfer.files.length) {
processSelectedFiles(e.dataTransfer.files);
}
});
fileInput.addEventListener('change', () => {
if (fileInput.files.length) {
processSelectedFiles(fileInput.files);
}
});
// 2. 验证并显示操作面板
function processSelectedFiles(files) {
filesArray = Array.from(files).filter(file => file.type.startsWith('image/'));
if (filesArray.length > 0) {
actionPanel.style.display = 'block';
statusText.innerText = `已选择 ${filesArray.length} 张图片。准备压缩。`;
}
}
// 3. 核心压缩引擎
function compressImage(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
// 将图像绘制到画布上
ctx.drawImage(img, 0, 0);
// 强制兼容性:将非常规格式转换为 JPEG 或 WebP
let mimeType = file.type;
if (mimeType !== 'image/jpeg' && mimeType !== 'image/webp') {
mimeType = 'image/jpeg';
}
// 使用 canvas.toBlob 方法以 70% 质量(0.7)进行压缩
canvas.toBlob((blob) => {
let finalName = file.name;
// 确保文件扩展名与新的 mimeType 匹配
if (mimeType === 'image/jpeg' && !finalName.match(/\.(jpg|jpeg)$/i)) {
finalName = finalName.substring(0, finalName.lastIndexOf('.')) + '.jpg';
}
resolve({ name: finalName, blob: blob });
}, mimeType, 0.7);
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
});
}
// 4. 处理批量 ZIP 打包
compressBtn.addEventListener('click', async () => {
if (filesArray.length === 0) return;
compressBtn.disabled = true;
try {
const zip = new JSZip();
const folder = zip.folder("compressed_images");
// 遍历每个文件并等待其压缩完成
for (let i = 0; i < filesArray.length; i++) {
statusText.innerText = `正在压缩 ${i + 1} / ${filesArray.length}...`;
const compressedData = await compressImage(filesArray[i]);
folder.file(compressedData.name, compressedData.blob);
}
statusText.innerText = '正在创建 ZIP 文件...';
// 生成并触发 ZIP 下载
const zipContent = await zip.generateAsync({ type: "blob" });
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(zipContent);
downloadLink.download = "compressed_images.zip";
downloadLink.click();
statusText.innerText = '下载成功!';
} catch (error) {
statusText.innerText = '压缩过程中发生错误。';
console.error(error);
} finally {
compressBtn.disabled = false;
}
});
});
</script>
JavaScript 逻辑解析:
拖拽事件:
dragover、dragleave和drop三个事件监听器配合使用,把用户从桌面拖进浏览器窗口的文件捕获进来。Canvas 技巧:浏览器本身并不能直接压缩文件,所以
compressImage函数会先读取图片,把它绘制到一个不可见的 HTML5<canvas>上,再通过canvas.toBlob()方法导出一份优化后的新图片。参数0.7表示压缩质量为 70%。扩展名处理:由于 canvas 只能导出 JPEG 或 WebP 格式的图片,脚本会主动检查并重命名文件扩展名(比如把
.png改成.jpg),然后再打包。异步打包:在
for循环中使用await逐张压缩图片。全部完成后,JSZip 把这些 blob 打包成一个文件夹,并在临时的<a>标签上程序化触发点击事件,从而下载最终的.zip文件。
结语
至此,你已经成功构建了一个快速的纯前端批量图片压缩工具。
借助 Canvas API 和 JSZip,你打造了一个非常实用的工具,既帮用户节省了带宽和存储空间,又不会泄露隐私。整个代码完全自包含、无需额外 HTML 样板,可以直接部署到几乎所有现代 Web 平台上。
如果想看这套代码在实际生产环境中的运行效果,可以试试这个在线 Image Compressor Tool。祝编程愉快!