图片处理测试工具
图片处理测试工具
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS 工具类兼容性测试 (String vs Blob/File)</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 20px; display: flex; gap: 30px; }
.panel { border: 1px solid #ddd; padding: 20px; border-radius: 8px; width: 45%; background: #fff; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
h3 { margin-top: 0; border-bottom: 2px solid #f0f0f0; padding-bottom: 10px; }
.preview-box { min-height: 200px; display: flex; align-items: center; justify-content: center; background: #f9f9f9; border: 1px dashed #ccc; margin: 10px 0; overflow: hidden; }
img, canvas { max-width: 100%; max-height: 400px; object-fit: contain; }
.log { font-family: monospace; font-size: 12px; color: #666; background: #eee; padding: 10px; border-radius: 4px; margin-top: 10px; word-break: break-all; }
.controls { margin: 15px 0; display: flex; gap: 10px; align-items: center; }
button { padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:disabled { background: #ccc; cursor: not-allowed; }
.tag { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 12px; font-weight: bold; }
.tag-png { background: #e3f2fd; color: #1565c0; }
.tag-jpg { background: #fff3e0; color: #ef6c00; }
.tag-err { background: #ffebee; color: #c62828; }
</style>
</head>
<body>
<!-- 左侧:输入控制 -->
<div class="panel">
<h3>1. 输入配置 & 触发</h3>
<div class="controls">
<label>选择图片:</label>
<input type="file" id="fileInput" accept="image/*">
</div>
<div class="controls">
<label>测试模式:</label>
<select id="modeSelect">
<option value="blob">模式 A: 传入 File/Blob 对象 (走 else 分支)</option>
<option value="url">模式 B: 传入 URL 字符串 (走 if 分支)</option>
</select>
</div>
<div id="fileInfo" style="margin-bottom: 10px; font-size: 0.9em;">
当前文件类型: <span id="currentType" class="tag">未选择</span>
</div>
<button id="runBtn" disabled>开始运行工具类</button>
<div id="log" class="log">等待操作...</div>
</div>
<!-- 右侧:结果展示 -->
<div class="panel">
<h3>2. 处理结果</h3>
<div id="status" style="font-weight: bold; margin-bottom: 10px;">-</div>
<div class="preview-box" id="outputBox">
<span style="color:#999">结果将显示在这里</span>
</div>
<button id="downloadBtn" style="display:none; margin-top:10px; background:#28a745;">下载结果</button>
</div>
<script>
// ==========================================
// 👇 模拟你的工具类 (严格复刻你提供的逻辑)
// ==========================================
// 辅助函数:模拟 urlToImg
async function urlToImg(url) {
console.log(" [Tool] 检测到 string 类型,执行 urlToImg...");
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => resolve(img);
img.onerror = () => reject(new Error("URL 加载失败"));
img.src = url;
});
}
// 辅助函数:模拟 blobToImg
async function blobToImg(blob) {
console.log(" [Tool] 检测到 Blob/File 类型,执行 blobToImg...");
return new Promise((resolve, reject) => {
const img = new Image();
const url = URL.createObjectURL(blob);
img.onload = () => {
URL.revokeObjectURL(url); // 清理临时 URL
resolve(img);
};
img.onerror = () => reject(new Error("Blob 加载失败"));
img.src = url;
});
}
// === 你的核心工具类逻辑 ===
async function myWatermarkTool(image) {
console.log(` [Tool] 收到参数类型: ${typeof image}`, image);
let img; // 最终用于处理的 HTMLImageElement
// --- 你的判断逻辑开始 ---
if (typeof (image) === 'string') {
console.log(" [Tool] -> 进入分支: typeof === 'string'");
img = await urlToImg(image);
} else {
console.log(" [Tool] -> 进入分支: else (检查 image.type)");
// 关键检查:必须有 type 属性且为 png 或 jpeg
if (image && image.type && ['image/png', 'image/jpeg'].includes(image.type)) {
console.log(` [Tool] -> 类型校验通过: ${image.type}`);
img = await blobToImg(image);
} else {
const errMsg = `不支持该类型的文件加水印 (获取到的 type: ${image ? image.type : 'undefined'})`;
console.error(" [Tool] -> " + errMsg);
return Promise.reject(errMsg);
}
}
// --- 你的判断逻辑结束 ---
// 模拟水印处理 (在 Canvas 上画文字)
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// 添加水印
ctx.font = "bold 40px Arial";
ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
ctx.lineWidth = 2;
const text = "WATERMARK TEST";
const x = canvas.width / 2 - ctx.measureText(text).width / 2;
const y = canvas.height / 2;
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
return canvas;
}
// ==========================================
// 👇 测试 Harness 逻辑
// ==========================================
const fileInput = document.getElementById('fileInput');
const modeSelect = document.getElementById('modeSelect');
const runBtn = document.getElementById('runBtn');
const logDiv = document.getElementById('log');
const currentTypeSpan = document.getElementById('currentType');
const outputBox = document.getElementById('outputBox');
const statusDiv = document.getElementById('status');
const downloadBtn = document.getElementById('downloadBtn');
let selectedFile = null;
// 监听文件选择
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) {
selectedFile = null;
runBtn.disabled = true;
currentTypeSpan.className = 'tag';
currentTypeSpan.textContent = "未选择";
logDiv.textContent = "等待选择文件...";
return;
}
selectedFile = file;
runBtn.disabled = false;
// 显示文件类型标签
currentTypeSpan.textContent = file.type || "未知类型";
currentTypeSpan.className = 'tag ' + (file.type === 'image/png' ? 'tag-png' : file.type === 'image/jpeg' ? 'tag-jpg' : 'tag-err');
logDiv.innerHTML = `已选择文件:<br>名称: ${file.name}<br>类型: ${file.type}<br>大小: ${(file.size/1024).toFixed(2)} KB`;
// 重置结果区
outputBox.innerHTML = '<span style="color:#999">准备就绪</span>';
statusDiv.textContent = "-";
downloadBtn.style.display = 'none';
});
// 监听运行按钮
runBtn.addEventListener('click', async () => {
if (!selectedFile) return;
const mode = modeSelect.value;
let inputArg;
// 1. 根据模式准备参数
if (mode === 'blob') {
// 模式 A: 直接传 File 对象 (File 继承自 Blob,拥有 .type 属性)
inputArg = selectedFile;
logDiv.innerHTML += `<br><br><strong>即将传入:</strong> File Object (type: ${selectedFile.type})`;
} else {
// 模式 B: 生成 URL 字符串
inputArg = URL.createObjectURL(selectedFile);
logDiv.innerHTML += `<br><br><strong>即将传入:</strong> String URL (${inputArg.substring(0, 30)}...)`;
}
statusDiv.textContent = "处理中...";
statusDiv.style.color = "#333";
runBtn.disabled = true;
try {
// 2. 调用工具类
const resultCanvas = await myWatermarkTool(inputArg);
// 3. 展示成功结果
statusDiv.textContent = "✅ 成功!";
statusDiv.style.color = "green";
outputBox.innerHTML = '';
outputBox.appendChild(resultCanvas);
// 4. 准备下载
resultCanvas.toBlob((blob) => {
const url = URL.createObjectURL(blob);
downloadBtn.onclick = () => {
const a = document.createElement('a');
a.href = url;
a.download = `watermarked_${selectedFile.name}`;
a.click();
URL.revokeObjectURL(url);
};
downloadBtn.style.display = 'inline-block';
}, selectedFile.type);
if (mode === 'url') {
// 清理模式 B 创建的临时 URL
URL.revokeObjectURL(inputArg);
}
} catch (error) {
// 5. 展示错误
console.error(error);
statusDiv.textContent = "❌ 失败";
statusDiv.style.color = "red";
outputBox.innerHTML = `<div style="color:red; padding:20px;">${error.message}</div>`;
logDiv.innerHTML += `<br><strong style="color:red">错误详情:</strong> ${error.message}`;
} finally {
runBtn.disabled = false;
}
});
</script>
</body>
</html>

浙公网安备 33010602011771号