shuiyin / worker.js
Ethscriptions's picture
Upload 5 files
53b24b2 verified
importScripts('https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js');
self.onmessage = async function(e) {
const { type, payload } = e.data;
if (type === 'EXPORT_ZIP') {
const { items, zipName } = payload;
try {
const zip = new JSZip();
const usedNames = new Set();
for (let i = 0; i < items.length; i++) {
const item = items[i];
self.postMessage({ type: 'PROGRESS', text: `处理图片 ${i + 1}/${items.length}`, percent: (i / items.length) * 50 });
// Decode image efficiently
const bmp = await createImageBitmap(item.file);
const canvas = new OffscreenCanvas(bmp.width, bmp.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(bmp, 0, 0);
// Draw Watermark
drawWatermarkWorker(ctx, canvas.width, canvas.height, item.data);
// Convert to Blob
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.9 });
bmp.close(); // Free memory
// Prepare filename
const data = item.data;
const sanitize = (str) => (str || '').replace(/[\\/:*?"<>|]/g, '_');
const cinema = sanitize(data.cinema);
const movie = sanitize(data.movie);
let formattedTime = data.time.replace(/\s+/g, '_').replace(/:/g, '-');
const tickets = sanitize(data.tickets);
const baseName = `${cinema}_${movie}_${formattedTime}_共出票${tickets}张`;
let finalName = baseName;
let counter = 1;
while (usedNames.has(finalName)) {
counter++;
finalName = `${baseName}_${counter}`;
}
usedNames.add(finalName);
zip.file(`${finalName}.jpg`, blob);
}
self.postMessage({ type: 'PROGRESS', text: '正在生成 ZIP 文件...', percent: 50 });
const content = await zip.generateAsync({
type: "blob",
compression: "STORE" // Faster than DEFLATE, fine for JPEGs
}, (metadata) => {
self.postMessage({
type: 'PROGRESS',
text: `正在压缩... ${metadata.percent.toFixed(0)}%`,
percent: 50 + (metadata.percent * 0.5)
});
});
self.postMessage({ type: 'DONE', blob: content, filename: zipName });
} catch (error) {
self.postMessage({ type: 'ERROR', error: error.message });
}
} else if (type === 'EXPORT_SINGLE') {
const { item, filename } = payload;
try {
self.postMessage({ type: 'PROGRESS', text: '处理中...', percent: 50 });
const bmp = await createImageBitmap(item.file);
const canvas = new OffscreenCanvas(bmp.width, bmp.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(bmp, 0, 0);
drawWatermarkWorker(ctx, canvas.width, canvas.height, item.data);
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.9 });
bmp.close();
self.postMessage({ type: 'DONE_SINGLE', blob, filename });
} catch(error) {
self.postMessage({ type: 'ERROR', error: error.message });
}
} else if (type === 'EXPORT_ALL_SINGLE') {
const { items } = payload;
try {
for (let i = 0; i < items.length; i++) {
const item = items[i];
self.postMessage({ type: 'PROGRESS', text: `导出图片 ${i + 1}/${items.length}`, percent: (i / items.length) * 100 });
const bmp = await createImageBitmap(item.file);
const canvas = new OffscreenCanvas(bmp.width, bmp.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(bmp, 0, 0);
drawWatermarkWorker(ctx, canvas.width, canvas.height, item.data);
const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.9 });
bmp.close();
const data = item.data;
const sanitize = (str) => (str || '').replace(/[\\/:*?"<>|]/g, '_');
const cinema = sanitize(data.cinema);
const movie = sanitize(data.movie);
let formattedTime = data.time.replace(/\s+/g, '_').replace(/:/g, '-');
const tickets = sanitize(data.tickets);
const newName = `${cinema}_${movie}_${formattedTime}_共出票${tickets}张_${i+1}.jpg`;
self.postMessage({ type: 'DONE_SINGLE', blob, filename: newName });
}
self.postMessage({ type: 'DONE_ALL_SINGLE' });
} catch(error) {
self.postMessage({ type: 'ERROR', error: error.message });
}
}
};
function drawWatermarkWorker(ctx, width, height, data) {
const baseFontSize = Math.max(30, Math.floor(height * 0.03));
const fontSize = baseFontSize * (data.sizeScale || 1.0);
ctx.font = `bold ${fontSize}px sans-serif`;
ctx.textAlign = 'center';
ctx.fillStyle = data.color || '#ffffff';
ctx.strokeStyle = '#000000';
ctx.lineWidth = Math.max(2, Math.floor(fontSize / 15));
const movieTitle = data.movie ? `《${data.movie}》` : '';
const lines = [
data.cinema,
movieTitle,
data.time,
data.tickets ? `共出票${data.tickets}张` : '',
data.extra1,
data.extra2
];
const validLines = lines.filter(line => line && line.trim() !== '');
const xOffset = (data.offsetX || 0) * (width / 1000);
const yOffset = (data.offsetY || 0) * (height / 1000);
const lineHeight = fontSize * 1.5;
const totalTextHeight = validLines.length * lineHeight;
const startY = height - totalTextHeight - (height * 0.05) + yOffset;
const x = (width / 2) + xOffset;
for (let index = 0; index < validLines.length; index++) {
const line = validLines[index];
const y = startY + (index * lineHeight) + fontSize;
ctx.strokeText(line, x, y);
ctx.fillText(line, x, y);
}
}