Files
InfoGenie/InfoGenie-frontend/public/toolbox/图片处理/图片压缩处理/图片压缩处理.html
2026-04-01 22:03:57 +08:00

360 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>
:root {
--primary-color: #007aff;
--bg-color: #f5f5f7;
--card-bg: #ffffff;
--text-main: #333333;
--text-muted: #888888;
--border-color: #e5e5ea;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: var(--card-bg);
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
}
h1 {
text-align: center;
margin-bottom: 30px;
font-size: 24px;
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 20px;
}
.upload-area:hover, .upload-area.dragover {
border-color: var(--primary-color);
background-color: rgba(0, 122, 255, 0.05);
}
#fileInput {
display: none;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 30px;
padding: 20px;
background: #fafafa;
border-radius: 8px;
display: none; /* 默认隐藏,上传后显示 */
}
.control-group {
flex: 1;
min-width: 200px;
}
.control-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
input[type="range"] {
width: 100%;
margin-bottom: 5px;
}
select {
width: 100%;
padding: 8px;
border: 1px solid var(--border-color);
border-radius: 6px;
background: white;
}
.preview-area {
display: none; /* 默认隐藏 */
gap: 20px;
margin-bottom: 20px;
}
@media (min-width: 600px) {
.preview-area {
display: flex; /* 电脑端并排 */
}
}
.preview-box {
flex: 1;
background: #fafafa;
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 15px;
text-align: center;
margin-bottom: 20px; /* 手机端间距 */
}
.preview-box img {
max-width: 100%;
max-height: 300px;
border-radius: 4px;
margin-top: 10px;
object-fit: contain;
}
.info {
font-size: 14px;
color: var(--text-muted);
margin-top: 10px;
}
.btn {
display: inline-block;
background-color: var(--primary-color);
color: white;
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
transition: background-color 0.3s;
width: 100%;
text-align: center;
}
.btn:hover {
background-color: #005bb5;
}
.btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
#downloadBtn {
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1>图片压缩工具</h1>
<div class="upload-area" id="uploadArea">
<p>点击这里或将图片拖拽到此处上传</p>
<p style="font-size: 12px; color: #888; margin-top: 8px;">支持 JPG, PNG, WebP 等常见格式</p>
<input type="file" id="fileInput" accept="image/*">
</div>
<div class="controls" id="controls">
<div class="control-group">
<label for="qualitySlider">压缩质量: <span id="qualityValue">0.8</span></label>
<input type="range" id="qualitySlider" min="0.1" max="1" step="0.1" value="0.8">
<div style="font-size: 12px; color: #888;">数值越小,体积越小,画质越低</div>
</div>
<div class="control-group">
<label for="formatSelect">输出格式:</label>
<select id="formatSelect">
<option value="image/jpeg">JPEG (体积较小)</option>
<option value="image/webp">WebP (推荐,体积最小)</option>
<option value="image/png">PNG (保留透明度,体积较大)</option>
</select>
</div>
</div>
<div class="preview-area" id="previewArea">
<div class="preview-box">
<h3>原图</h3>
<div class="info" id="originalInfo">等待上传...</div>
<img id="originalImg" src="" alt="原图预览" style="display: none;">
</div>
<div class="preview-box">
<h3>压缩后</h3>
<div class="info" id="compressedInfo">等待处理...</div>
<img id="compressedImg" src="" alt="压缩后预览" style="display: none;">
</div>
</div>
<a id="downloadBtn" class="btn" download="compressed_image.jpg">下载压缩后的图片</a>
</div>
<script>
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const controls = document.getElementById('controls');
const previewArea = document.getElementById('previewArea');
const originalImg = document.getElementById('originalImg');
const compressedImg = document.getElementById('compressedImg');
const originalInfo = document.getElementById('originalInfo');
const compressedInfo = document.getElementById('compressedInfo');
const qualitySlider = document.getElementById('qualitySlider');
const qualityValue = document.getElementById('qualityValue');
const formatSelect = document.getElementById('formatSelect');
const downloadBtn = document.getElementById('downloadBtn');
let currentFile = null;
// 格式化文件大小
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
// 处理上传点击和拖拽
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
if (e.dataTransfer.files.length > 0) {
handleFile(e.dataTransfer.files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
// 接收文件并读取
function handleFile(file) {
if (!file.type.startsWith('image/')) {
alert('请上传图片文件!');
return;
}
currentFile = file;
// 显示原图信息
originalInfo.innerText = `大小: ${formatBytes(file.size)} \n格式: ${file.type.split('/')[1].toUpperCase()}`;
const reader = new FileReader();
reader.onload = (e) => {
originalImg.src = e.target.result;
originalImg.style.display = 'inline-block';
// 显示控制面板和预览区
controls.style.display = 'flex';
previewArea.style.display = 'flex';
// 默认选中 WebP如果原图是 PNG 且用户可能需要透明度,可以在这里做额外判断
if(file.type === 'image/png') {
formatSelect.value = 'image/png';
} else {
formatSelect.value = 'image/jpeg';
}
compressImage();
};
reader.readAsDataURL(file);
}
// 监听调节事件
qualitySlider.addEventListener('input', (e) => {
qualityValue.innerText = e.target.value;
});
qualitySlider.addEventListener('change', compressImage);
formatSelect.addEventListener('change', compressImage);
// 核心压缩逻辑 (利用 Canvas)
function compressImage() {
if (!currentFile || !originalImg.src) return;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 创建一个新的 Image 对象用来绘制
const img = new Image();
img.onload = () => {
// 保持原始宽高
canvas.width = img.width;
canvas.height = img.height;
// 如果导出为 JPEG将透明背景填充为白色
if (formatSelect.value === 'image/jpeg') {
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const quality = parseFloat(qualitySlider.value);
const outputFormat = formatSelect.value;
// 将 canvas 转为 Blob
canvas.toBlob((blob) => {
if(!blob) return;
// 显示压缩后信息
compressedInfo.innerText = `大小: ${formatBytes(blob.size)} \n格式: ${outputFormat.split('/')[1].toUpperCase()}`;
// 计算压缩率
const ratio = ((1 - (blob.size / currentFile.size)) * 100).toFixed(1);
if(ratio > 0) {
compressedInfo.innerText += `\n节省了 ${ratio}% 的空间`;
}
// 创建预览 URL 和下载链接
const compressUrl = URL.createObjectURL(blob);
compressedImg.src = compressUrl;
compressedImg.style.display = 'inline-block';
downloadBtn.style.display = 'block';
downloadBtn.href = compressUrl;
// 设置下载文件名
const ext = outputFormat.split('/')[1];
const originalName = currentFile.name.split('.')[0];
downloadBtn.download = `${originalName}_compressed.${ext}`;
}, outputFormat, quality);
};
img.src = originalImg.src;
}
</script>
</body>
</html>