Files

56 lines
3.0 KiB
JavaScript
Raw Permalink 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.
#!/usr/bin/env node
/**
* gen_deck_thumbs.mjs — 为多文件 deck 每页生成缩略图(给 deck_index.html 的「无限画廊」概览用)。
*
* 背景deck_index.html 有两种概览——
* · 网格 grid默认 60%):用 iframe 渲染真实子页面,清晰、所见即所得,无需缩略图。
* · 无限画廊 gallery40%):把所有页无缝无限平铺 + 缓慢漂移,几十~上百个瓦片若都用 iframe 会很卡,
* 所以画廊改用 <img> 缩略图——同一张图复用多次浏览器只解码一次,流畅。
* 本脚本就是给画廊准备这批缩略图。grid 模式不需要它。
*
* 用法(复制到 deck 项目根目录,装依赖后运行):
* npm install playwright sharp
* node gen_deck_thumbs.mjs --slides slides --out thumbs [--width 1600] [--quality 86]
*
* 然后在 index.html 的 MANIFEST 给每项加 thumb与 file 同名 .jpg
* { file: "slides/01-cover.html", thumb: "thumbs/01-cover.jpg", label: "封面" }
* deck_index.html 仅在画廊模式用 thumb网格模式始终用 file(iframe)。没有 thumb 时画廊回退 iframe。
*
* 提示:缩略图分辨率别太低(默认 1600px否则画廊里卡片 hover 放大后会发虚。
*/
import { chromium } from 'playwright';
import sharp from 'sharp';
import fs from 'fs';
import path from 'path';
const arg = (n, d) => { const i = process.argv.indexOf('--' + n); return i > -1 && process.argv[i + 1] ? process.argv[i + 1] : d; };
const slidesDir = arg('slides', 'slides');
const outDir = arg('out', 'thumbs');
const width = parseInt(arg('width', '1600'), 10);
const quality = parseInt(arg('quality', '86'), 10);
const W = parseInt(arg('canvas-w', '1920'), 10);
const H = parseInt(arg('canvas-h', '1080'), 10);
if (!fs.existsSync(slidesDir)) { console.error('找不到 slides 目录: ' + slidesDir); process.exit(1); }
fs.mkdirSync(outDir, { recursive: true });
const files = fs.readdirSync(slidesDir).filter(f => f.endsWith('.html')).sort();
if (!files.length) { console.error('slides 目录里没有 .html'); process.exit(1); }
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: W, height: H }, deviceScaleFactor: 1 });
let ok = 0;
for (const f of files) {
const base = f.replace(/\.html$/, '');
const out = path.join(outDir, base + '.jpg');
try {
await page.goto('file://' + path.resolve(slidesDir, f), { waitUntil: 'load' });
await page.waitForTimeout(2800); // 等 webfont / 图片 paint
const buf = await page.screenshot({ type: 'png', clip: { x: 0, y: 0, width: W, height: H } });
await sharp(buf).resize(width).jpeg({ quality }).toFile(out);
ok++; console.log('[ok] ' + out);
} catch (e) { console.error('[FAIL] ' + f + ': ' + e.message); }
}
await browser.close();
console.log(`\n=== ${ok}/${files.length} 张缩略图 → ${outDir}/ ===`);
console.log('在 index.html 的 MANIFEST 每项加 thumb: "' + outDir + '/<同名>.jpg"(仅画廊模式用到)');