图片处理测试工具

图片处理测试工具

<!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>
posted @ 2026-03-12 16:04  庶旁  阅读(4)  评论(0)    收藏  举报