点击关注上方“消遣杂谈”,关注后了解更多内容。

前言:根据丈母娘的需求,我借鉴了相声大哥的思路,我寻思能不能用html制作一个图片、PDF加水印的工具能,说干就干,然后人工智能就给我搞好了。特写此文,我会把代码贴出来,从此不求人
。文中带小广告。排版进行了重新调整。
使用非常简单,无论是win系统还是银河麒麟系统重,自己将代码(代码在后面)复制进入txt文本,然后改后缀名为html即可



那么人工智能就能帮我完成我的诉求
我不是一个专业的编程人员,我也没有那么多专业的知识,但是我是个材料
,我知道自己如何将自己的诉求表达清楚,我掌握了很基础的一些知识,能初步看懂这些底层代码。因为自己预览,感觉影响观感,所以代码直接放后面,本来想着要不要删除算了,觉得这样比较快,就放着吧。
<!DOCTYPE html><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>消遣杂谈- 图片与PDF水印工具</title><linkrel="preconnect"href="https://fonts.googleapis.com"><linkrel="preconnect"href="https://fonts.gstatic.com"crossorigin><linkhref="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap"rel="stylesheet"><scriptsrc="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script><style>* { margin: 0; padding: 0; box-sizing: border-box; }:root {--primary: #4f46e5;--primary-light: #6366f1;--primary-dark: #4338ca;--bg: #f0f2f5;--card-bg: #ffffff;--text: #1e293b;--text-light: #64748b;--border: #e2e8f0;--success: #10b981;--danger: #ef4444;--radius: 12px;--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);--shadow-lg: 0 10px 30px rgba(0,0,0,0.1);}body {font-family: 'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;background: var(--bg);color: var(--text);min-height: 100vh;line-height: 1.6;}/* ===== Header ===== */header {background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);color: white;padding: 28px 24px;text-align: center;box-shadow: 0 4px 20px rgba(79, 70, 229, 0.25);}header h1 {font-size: 1.8rem;font-weight: 700;margin-bottom: 6px;}header p {font-size: 0.95rem;opacity: 0.9;}/* ===== Layout ===== */.container {max-width: 1100px;margin: 0 auto;padding: 24px 20px 60px;}.main-grid {display: grid;grid-template-columns: 1fr 1fr;gap: 20px;}@media (max-width: 820px) {.main-grid { grid-template-columns: 1fr; }}.card {background: var(--card-bg);border-radius: var(--radius);padding: 24px;box-shadow: var(--shadow);}.card h2 {font-size: 1.15rem;font-weight: 700;margin-bottom: 18px;display: flex;align-items: center;gap: 8px;}.card h2 .icon {width: 28px;height: 28px;border-radius: 8px;background: linear-gradient(135deg, var(--primary), var(--primary-light));display: inline-flex;align-items: center;justify-content: center;color: white;font-size: 0.8rem;flex-shrink: 0;}/* ===== Upload Zone ===== */.upload-zone {border: 2px dashed var(--border);border-radius: var(--radius);padding: 36px 20px;text-align: center;cursor: pointer;transition: all 0.25s ease;background: #fafbfc;}.upload-zone:hover {border-color: var(--primary-light);background: #f5f3ff;}.upload-zone.dragover {border-color: var(--primary);background: #eef2ff;transform: scale(1.01);}.upload-zone .upload-icon {font-size: 2.5rem;margin-bottom: 8px;}.upload-zone p {color: var(--text-light);font-size: 0.9rem;}.upload-zone .browse-link {color: var(--primary);font-weight: 500;text-decoration: underline;}.upload-zone .formats {font-size: 0.8rem;color: var(--text-light);margin-top: 6px;}/* ===== File List ===== */.file-list {margin-top: 16px;display: flex;flex-direction: column;gap: 8px;}.file-item {display: flex;align-items: center;gap: 10px;padding: 10px 14px;background: #f8fafc;border: 1px solid var(--border);border-radius: 8px;font-size: 0.875rem;transition: all 0.2s;}.file-item:hover { border-color: var(--primary-light); }.file-item .file-type-badge {padding: 2px 8px;border-radius: 6px;font-size: 0.7rem;font-weight: 600;text-transform: uppercase;color: white;flex-shrink: 0;}.file-item .file-type-badge.img { background: #3b82f6; }.file-item .file-type-badge.pdf { background: #ef4444; }.file-item .file-name {flex: 1;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.file-item .file-size {color: var(--text-light);font-size: 0.78rem;flex-shrink: 0;}.file-item .remove-btn {background: none;border: none;color: var(--text-light);cursor: pointer;font-size: 1.1rem;padding: 2px 6px;border-radius: 4px;transition: all 0.2s;}.file-item .remove-btn:hover {color: var(--danger);background: #fef2f2;}/* ===== Settings ===== */.settings-group {display: flex;flex-direction: column;gap: 18px;}.setting-row {display: flex;flex-direction: column;gap: 6px;}.setting-row label {font-size: 0.85rem;font-weight: 500;color: var(--text);display: flex;justify-content: space-between;align-items: center;}.setting-row label .value-tag {background: var(--primary);color: white;padding: 1px 8px;border-radius: 6px;font-size: 0.75rem;font-weight: 600;min-width: 36px;text-align: center;}.setting-row input[type="text"] {padding: 10px 14px;border: 1px solid var(--border);border-radius: 8px;font-size: 0.9rem;font-family: inherit;transition: border-color 0.2s;}.setting-row input[type="text"]:focus {outline: none;border-color: var(--primary);box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);}.setting-row input[type="range"] {-webkit-appearance: none;appearance: none;width: 100%;height: 6px;background: var(--border);border-radius: 3px;outline: none;}.setting-row input[type="range"]::-webkit-slider-thumb {-webkit-appearance: none;appearance: none;width: 18px;height: 18px;background: var(--primary);border-radius: 50%;cursor: pointer;border: 2px solid white;box-shadow: 0 2px 4px rgba(0,0,0,0.15);transition: transform 0.15s;}.setting-row input[type="range"]::-webkit-slider-thumb:hover {transform: scale(1.2);}.setting-row input[type="range"]::-moz-range-thumb {width: 18px;height: 18px;background: var(--primary);border-radius: 50%;cursor: pointer;border: 2px solid white;box-shadow: 0 2px 4px rgba(0,0,0,0.15);}.setting-row input[type="color"] {width: 100%;height: 40px;border: 1px solid var(--border);border-radius: 8px;cursor: pointer;background: white;padding: 4px;}.setting-row select {padding: 10px 14px;border: 1px solid var(--border);border-radius: 8px;font-size: 0.9rem;font-family: inherit;background: white;cursor: pointer;}.setting-row select:focus {outline: none;border-color: var(--primary);}.setting-inline {display: flex;gap: 12px;}.setting-inline > .setting-row { flex: 1; }/* ===== Preview ===== */.preview-wrapper {margin-top: 16px;border-radius: var(--radius);overflow: hidden;background: #f1f5f9;border: 1px solid var(--border);display: flex;justify-content: center;align-items: center;min-height: 200px;position: relative;}.preview-wrapper canvas {max-width: 100%;height: auto;display: block;}.preview-placeholder {color: var(--text-light);font-size: 0.9rem;padding: 40px;}/* ===== Buttons ===== */.btn {padding: 12px 28px;border: none;border-radius: 8px;font-size: 0.95rem;font-weight: 600;font-family: inherit;cursor: pointer;transition: all 0.2s ease;display: inline-flex;align-items: center;justify-content: center;gap: 8px;}.btn-primary {background: linear-gradient(135deg, var(--primary), var(--primary-light));color: white;box-shadow: 0 4px 14px rgba(79, 70, 229, 0.3);}.btn-primary:hover {transform: translateY(-1px);box-shadow: 0 6px 20px rgba(79, 70, 229, 0.4);}.btn-primary:active { transform: translateY(0); }.btn-primary:disabled {opacity: 0.5;cursor: not-allowed;transform: none;box-shadow: none;}.btn-secondary {background: white;color: var(--text);border: 1px solid var(--border);}.btn-secondary:hover {border-color: var(--primary);color: var(--primary);}.btn-sm {padding: 7px 16px;font-size: 0.82rem;}.btn-success {background: var(--success);color: white;}.btn-success:hover { background: #059669; }.process-bar {margin-top: 20px;display: flex;gap: 12px;align-items: center;}/* ===== Results ===== */.results-section {margin-top: 28px;}.results-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 16px;}.results-header h2 {font-size: 1.2rem;font-weight: 700;}.results-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));gap: 16px;}.result-card {background: var(--card-bg);border-radius: var(--radius);overflow: hidden;box-shadow: var(--shadow);transition: all 0.2s;}.result-card:hover {box-shadow: var(--shadow-lg);transform: translateY(-2px);}.result-thumb {width: 100%;height: 180px;background: #f1f5f9;display: flex;justify-content: center;align-items: center;overflow: hidden;position: relative;}.result-thumb img {max-width: 100%;max-height: 100%;object-fit: contain;}.result-thumb .pdf-placeholder {font-size: 3rem;}.result-info {padding: 12px 14px;}.result-info .name {font-size: 0.82rem;font-weight: 500;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;margin-bottom: 8px;}.result-info .actions {display: flex;gap: 8px;}/* ===== Progress ===== */.progress-overlay {position: fixed;top: 0; left: 0; right: 0; bottom: 0;background: rgba(0,0,0,0.5);display: none;justify-content: center;align-items: center;z-index: 1000;}.progress-overlay.active { display: flex; }.progress-box {background: white;padding: 32px 40px;border-radius: var(--radius);text-align: center;box-shadow: var(--shadow-lg);}.spinner {width: 48px;height: 48px;border: 4px solid var(--border);border-top-color: var(--primary);border-radius: 50%;animation: spin 0.8s linear infinite;margin: 0 auto 16px;}@keyframes spin { to { transform: rotate(360deg); } }.progress-text {font-size: 0.95rem;color: var(--text);}.progress-detail {font-size: 0.82rem;color: var(--text-light);margin-top: 4px;}/* ===== Empty State ===== */.empty-state {text-align: center;padding: 40px 20px;color: var(--text-light);}.empty-state .emoji {font-size: 2.5rem;margin-bottom: 8px;}/* ===== Toast ===== */.toast {position: fixed;bottom: 24px;left: 50%;transform: translateX(-50%) translateY(100px);background: var(--text);color: white;padding: 12px 24px;border-radius: 8px;font-size: 0.88rem;z-index: 2000;opacity: 0;transition: all 0.3s ease;box-shadow: var(--shadow-lg);}.toast.show {opacity: 1;transform: translateX(-50%) translateY(0);}.toast.error { background: var(--danger); }.toast.success { background: var(--success); }</style></head><body><header><h1>消遣杂谈制作的水印工具@HBIKO</h1><p>上传图片或PDF,自定义水印文字,一键导出水印文件</p></header><divclass="container"><divclass="main-grid"><!-- Left: Upload + Files --><divclass="card"><h2><spanclass="icon">1</span>上传文件</h2><divclass="upload-zone"id="uploadZone"><divclass="upload-icon">📁</div><p>将文件拖拽到此处,或 <spanclass="browse-link">点击浏览</span></p><divclass="formats">支持 PNG / JPG / JPEG / GIF / WEBP / PDF</div></div><inputtype="file"id="fileInput"multipleaccept="image/png,image/jpeg,image/gif,image/webp,application/pdf"hidden><divclass="file-list"id="fileList"></div></div><!-- Right: Settings + Preview --><divclass="card"><h2><spanclass="icon">2</span>水印设置</h2><divclass="settings-group"><divclass="setting-row"><label>水印文字</label><inputtype="text"id="wmText"value="仅供预览 严禁商用"placeholder="请输入水印文字..."></div><divclass="setting-inline"><divclass="setting-row"><label>字体大小 <spanclass="value-tag"id="fontSizeTag">28</span></label><inputtype="range"id="fontSize"min="10"max="80"value="28"></div><divclass="setting-row"><label>透明度 <spanclass="value-tag"id="opacityTag">30%</span></label><inputtype="range"id="opacity"min="5"max="100"value="30"></div></div><divclass="setting-inline"><divclass="setting-row"><label>旋转角度 <spanclass="value-tag"id="rotationTag">-30°</span></label><inputtype="range"id="rotation"min="-90"max="90"value="-30"></div><divclass="setting-row"><label>文字颜色</label><inputtype="color"id="color"value="#808080"></div></div><divclass="setting-inline"><divclass="setting-row"><label>布局模式</label><selectid="layoutMode"><optionvalue="tile">平铺(满屏重复)</option><optionvalue="center">居中(单个水印)</option></select></div><divclass="setting-row"id="densityRow"><label>平铺密度 <spanclass="value-tag"id="densityTag">中</span></label><inputtype="range"id="density"min="1"max="5"value="3"></div></div><divclass="setting-row"><label>图片导出格式</label><selectid="imgFormat"><optionvalue="image/png">PNG(无损,支持透明)</option><optionvalue="image/jpeg">JPEG(体积小)</option></select></div></div><h2style="margin-top: 24px;"><spanclass="icon">3</span>实时预览</h2><divclass="preview-wrapper"id="previewWrapper"><divclass="preview-placeholder">输入水印文字后此处显示预览效果</div></div></div></div><!-- Process Button --><divclass="process-bar"><buttonclass="btn btn-primary"id="processBtn"disabled><span>✨ 添加水印并导出</span></button><buttonclass="btn btn-secondary"id="clearBtn">清空文件</button><spanid="fileCount"style="font-size: 0.85rem; color: var(--text-light);"></span></div><!-- Results --><divclass="results-section"id="resultsSection"style="display: none;"><divclass="results-header"><h2>处理结果</h2><buttonclass="btn btn-success btn-sm"id="downloadAllBtn">⬇ 全部下载</button></div><divclass="results-grid"id="resultsGrid"></div></div></div><!-- Progress Overlay --><divclass="progress-overlay"id="progressOverlay"><divclass="progress-box"><divclass="spinner"></div><divclass="progress-text"id="progressText">正在处理文件...</div><divclass="progress-detail"id="progressDetail"></div></div></div><!-- Toast --><divclass="toast"id="toast"></div><script>(function() {'use strict';// ===== State =====let files = [];let results = [];let fontReady = false;// ===== DOM =====const uploadZone = document.getElementById('uploadZone');const fileInput = document.getElementById('fileInput');const fileList = document.getElementById('fileList');const fileCount = document.getElementById('fileCount');const processBtn = document.getElementById('processBtn');const clearBtn = document.getElementById('clearBtn');const resultsSection = document.getElementById('resultsSection');const resultsGrid = document.getElementById('resultsGrid');const downloadAllBtn = document.getElementById('downloadAllBtn');const progressOverlay = document.getElementById('progressOverlay');const progressText = document.getElementById('progressText');const progressDetail = document.getElementById('progressDetail');const toast = document.getElementById('toast');// Settingsconst wmText = document.getElementById('wmText');const fontSize = document.getElementById('fontSize');const fontSizeTag = document.getElementById('fontSizeTag');const opacity = document.getElementById('opacity');const opacityTag = document.getElementById('opacityTag');const rotation = document.getElementById('rotation');const rotationTag = document.getElementById('rotationTag');const color = document.getElementById('color');const layoutMode = document.getElementById('layoutMode');const density = document.getElementById('density');const densityTag = document.getElementById('densityTag');const densityRow = document.getElementById('densityRow');const imgFormat = document.getElementById('imgFormat');const previewWrapper = document.getElementById('previewWrapper');const densityLabels = { 1: '疏', 2: '较疏', 3: '中', 4: '较密', 5: '密' };// ===== Init Font =====async function initFont() {try {await document.fonts.load('700 28px "Noto Sans SC"');await document.fonts.load('400 28px "Noto Sans SC"');fontReady = true;} catch(e) {console.warn('Font load failed, using fallback:', e);fontReady = true;}updatePreview();}// ===== Toast =====function showToast(msg, type) {toast.textContent = msg;toast.className = 'toast show' + (type ? ' ' + type : '');setTimeout(() => { toast.className = 'toast'; }, 3000);}// ===== Settings Object =====function getSettings() {return {text: wmText.value || '水印',fontSize: parseInt(fontSize.value),opacity: parseInt(opacity.value) / 100,rotation: parseInt(rotation.value),color: color.value,layoutMode: layoutMode.value,density: parseInt(density.value),imgFormat: imgFormat.value};}// ===== Watermark Canvas Drawing =====function drawWatermarkOnCanvas(ctx, width, height, settings) {const s = settings;ctx.save();ctx.fillStyle = s.color;ctx.globalAlpha = s.opacity;ctx.font = '700 ' + s.fontSize + 'px "Noto Sans SC", sans-serif';ctx.textAlign = 'center';ctx.textBaseline = 'middle';const rad = s.rotation * Math.PI / 180;const textWidth = ctx.measureText(s.text).width;const stepX = textWidth + s.fontSize * (7 - s.density);const stepY = s.fontSize * (4 + (5 - s.density) * 1.5);if (s.layoutMode === 'center') {ctx.translate(width / 2, height / 2);ctx.rotate(rad);ctx.fillText(s.text, 0, 0);} else {// Tile mode: cover entire canvas, including edges after rotationconst diag = Math.sqrt(width * width + height * height);const cols = Math.ceil(diag / stepX) + 2;const rows = Math.ceil(diag / stepY) + 2;const startX = (width / 2) - (cols * stepX) / 2;const startY = (height / 2) - (rows * stepY) / 2;for (let r = 0; r < rows; r++) {// Offset every other row for a nicer patternconst offsetX = (r % 2) * (stepX / 2);for (let c = 0; c < cols; c++) {const x = startX + c * stepX + offsetX;const y = startY + r * stepY;ctx.save();ctx.translate(x, y);ctx.rotate(rad);ctx.fillText(s.text, 0, 0);ctx.restore();}}}ctx.restore();}// Creates a transparent watermark overlay canvasfunction createWatermarkCanvas(width, height, settings, scale) {scale = scale || 1;const canvas = document.createElement('canvas');canvas.width = Math.round(width * scale);canvas.height = Math.round(height * scale);const ctx = canvas.getContext('2d');if (scale !== 1) {ctx.scale(scale, scale);}drawWatermarkOnCanvas(ctx, width, height, settings);return canvas;}// ===== Preview =====function updatePreview() {const s = getSettings();if (!s.text.trim()) {previewWrapper.innerHTML = '<div class="preview-placeholder">请输入水印文字</div>';return;}const pw = 480, ph = 270;const canvas = document.createElement('canvas');canvas.width = pw;canvas.height = ph;const ctx = canvas.getContext('2d');// Draw a sample backgroundconst grad = ctx.createLinearGradient(0, 0, pw, ph);grad.addColorStop(0, '#e0e7ff');grad.addColorStop(1, '#f0f9ff');ctx.fillStyle = grad;ctx.fillRect(0, 0, pw, ph);// Draw sample content linesctx.strokeStyle = 'rgba(100,116,139,0.15)';ctx.lineWidth = 1;for (let i = 1; i <= 6; i++) {const y = (ph / 7) * i;ctx.beginPath();ctx.moveTo(40, y);ctx.lineTo(pw - 40, y);ctx.stroke();}// Draw watermarkdrawWatermarkOnCanvas(ctx, pw, ph, s);previewWrapper.innerHTML = '';previewWrapper.appendChild(canvas);}// ===== File Handling =====function handleFiles(fileListObj) {const newFiles = Array.from(fileListObj);let added = 0;for (const f of newFiles) {if (files.some(existing => existing.name === f.name && existing.size === f.size)) {continue;}if (f.type.startsWith('image/') || f.type === 'application/pdf') {files.push(f);added++;}}if (added > 0) {showToast('已添加 ' + added + ' 个文件', 'success');}renderFileList();updateButtons();}function renderFileList() {fileList.innerHTML = '';files.forEach((f, i) => {const item = document.createElement('div');item.className = 'file-item';const isPDF = f.type === 'application/pdf';const badgeClass = isPDF ? 'pdf' : 'img';const badgeText = isPDF ? 'PDF' : 'IMG';item.innerHTML ='<span class="file-type-badge ' + badgeClass + '">' + badgeText + '</span>' +'<span class="file-name">' + escapeHtml(f.name) + '</span>' +'<span class="file-size">' + formatSize(f.size) + '</span>' +'<button class="remove-btn" data-index="' + i + '" title="移除">✕</button>';fileList.appendChild(item);});fileList.querySelectorAll('.remove-btn').forEach(btn => {btn.addEventListener('click', function() {const idx = parseInt(this.dataset.index);files.splice(idx, 1);renderFileList();updateButtons();});});}function updateButtons() {processBtn.disabled = files.length === 0;fileCount.textContent = files.length > 0 ? '共 ' + files.length + ' 个文件' : '';}// ===== Process Files =====async function processAll() {if (files.length === 0) return;results = [];resultsGrid.innerHTML = '';const settings = getSettings();if (!settings.text.trim()) {showToast('请输入水印文字', 'error');return;}progressOverlay.classList.add('active');for (let i = 0; i < files.length; i++) {const f = files[i];progressText.textContent = '正在处理文件 (' + (i + 1) + '/' + files.length + ')';progressDetail.textContent = f.name;try {if (f.type === 'application/pdf') {await processPDF(f, settings);} else {await processImage(f, settings);}} catch(err) {console.error('Error processing', f.name, err);showToast('处理失败: ' + f.name, 'error');}}progressOverlay.classList.remove('active');renderResults();if (results.length > 0) {resultsSection.style.display = 'block';showToast('完成!共处理 ' + results.length + ' 个文件', 'success');resultsSection.scrollIntoView({ behavior: 'smooth' });}}// ===== Process Image =====function processImage(file, settings) {return new Promise((resolve, reject) => {const img = new Image();const url = URL.createObjectURL(file);img.onload = function() {try {const canvas = document.createElement('canvas');canvas.width = img.naturalWidth;canvas.height = img.naturalHeight;const ctx = canvas.getContext('2d');ctx.drawImage(img, 0, 0);// Draw watermarkdrawWatermarkOnCanvas(ctx, canvas.width, canvas.height, settings);// Exportconst format = settings.imgFormat;const ext = format === 'image/png' ? 'png' : 'jpg';const baseName = file.name.replace(/\.[^.]+$/, '');const outName = baseName + '_watermarked.' + ext;canvas.toBlob(function(blob) {URL.revokeObjectURL(url);if (blob) {// Create thumbnailconst thumbUrl = URL.createObjectURL(blob);results.push({name: outName,blob: blob,thumbUrl: thumbUrl,isImage: true});}resolve();}, format, 0.92);} catch(err) {URL.revokeObjectURL(url);reject(err);}};img.onerror = function() {URL.revokeObjectURL(url);reject(new Error('图片加载失败'));};img.src = url;});}// ===== Process PDF =====async function processPDF(file, settings) {const { PDFDocument } = PDFLib;const arrayBuffer = await file.arrayBuffer();const pdfDoc = await PDFDocument.load(arrayBuffer, { ignoreEncryption: true });const pages = pdfDoc.getPages();for (const page of pages) {const { width, height } = page.getSize();// Create watermark overlay canvas at 2x scale for sharpnessconst scale = 2;const wmCanvas = createWatermarkCanvas(width, height, settings, scale);// Convert canvas to PNG bytesconst pngBlob = await new Promise(function(resolve) {wmCanvas.toBlob(resolve, 'image/png');});const pngBytes = await pngBlob.arrayBuffer();// Embed PNG into PDFconst pngImage = await pdfDoc.embedPng(pngBytes);// Draw watermark image over the page (on top of existing content)page.drawImage(pngImage, {x: 0,y: 0,width: width,height: height});}// Saveconst pdfBytes = await pdfDoc.save();const blob = new Blob([pdfBytes], { type: 'application/pdf' });const baseName = file.name.replace(/\.[^.]+$/, '');const outName = baseName + '_watermarked.pdf';results.push({name: outName,blob: blob,thumbUrl: null,isImage: false});}// ===== Render Results =====function renderResults() {resultsGrid.innerHTML = '';results.forEach((r, i) => {const card = document.createElement('div');card.className = 'result-card';let thumbHtml;if (r.isImage) {thumbHtml = '<img src="' + r.thumbUrl + '" alt="预览">';} else {thumbHtml = '<div class="pdf-placeholder">📄</div>';}card.innerHTML ='<div class="result-thumb">' + thumbHtml + '</div>' +'<div class="result-info">' +'<div class="name">' + escapeHtml(r.name) + '</div>' +'<div class="actions">' +'<button class="btn btn-primary btn-sm" data-index="' + i + '">⬇ 下载</button>' +'</div>' +'</div>';resultsGrid.appendChild(card);});resultsGrid.querySelectorAll('button[data-index]').forEach(btn => {btn.addEventListener('click', function() {downloadResult(parseInt(this.dataset.index));});});}function downloadResult(index) {const r = results[index];if (!r) return;const url = URL.createObjectURL(r.blob);const a = document.createElement('a');a.href = url;a.download = r.name;document.body.appendChild(a);a.click();document.body.removeChild(a);setTimeout(() => URL.revokeObjectURL(url), 1000);}function downloadAll() {if (results.length === 0) return;results.forEach((r, i) => {setTimeout(() => downloadResult(i), i * 300);});showToast('开始下载全部文件...', 'success');}// ===== Utility =====function formatSize(bytes) {if (bytes < 1024) return bytes + ' B';if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';return (bytes / 1048576).toFixed(1) + ' MB';}function escapeHtml(str) {const div = document.createElement('div');div.textContent = str;return div.innerHTML;}// ===== Event Listeners =====uploadZone.addEventListener('click', () => fileInput.click());fileInput.addEventListener('change', function() {if (this.files.length > 0) handleFiles(this.files);this.value = '';});uploadZone.addEventListener('dragover', function(e) {e.preventDefault();this.classList.add('dragover');});uploadZone.addEventListener('dragleave', function() {this.classList.remove('dragover');});uploadZone.addEventListener('drop', function(e) {e.preventDefault();this.classList.remove('dragover');if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files);});// Settings listenersfontSize.addEventListener('input', function() {fontSizeTag.textContent = this.value;updatePreview();});opacity.addEventListener('input', function() {opacityTag.textContent = this.value + '%';updatePreview();});rotation.addEventListener('input', function() {rotationTag.textContent = this.value + '°';updatePreview();});color.addEventListener('input', updatePreview);wmText.addEventListener('input', updatePreview);layoutMode.addEventListener('change', function() {densityRow.style.display = this.value === 'tile' ? 'flex' : 'none';updatePreview();});density.addEventListener('input', function() {densityTag.textContent = densityLabels[this.value];updatePreview();});imgFormat.addEventListener('change', updatePreview);processBtn.addEventListener('click', processAll);clearBtn.addEventListener('click', function() {if (files.length === 0) return;files = [];results = [];renderFileList();updateButtons();resultsSection.style.display = 'none';showToast('已清空文件列表');});downloadAllBtn.addEventListener('click', downloadAll);// InitdensityRow.style.display = 'flex';initFont();})();</script></body></html>
以前写的少,现在写的多,很多时候我还有很多文章,工作很忙,实在没空写,写了个题目,好多想写的,需要时间梳理。
最后希望大家通过这个文章能有所收获,让我们一起变得更强


如果觉得不错,给我点个赞,分享给身边的人,我最近把我写的文章都导入到了ima知识库,强烈推荐看我置顶的文章,基本上我自己的ima知识库,对我来说非常好用。
觉得内容好看
点个
和
↓↓
夜雨聆风