tools/serve.mjs 本地静态服务 + 测试驱动注入(模拟 WE 下发用户属性、冻结动画相位、藏 canvas 取参考帧) tools/cdp.mjs 最小 CDP 客户端(真实时间等待;--screenshot 的虚拟时间会在骨骼加载完成前就耗尽) tools/shot.mjs 截图/取状态,就绪判据可传入 tools/diff.mjs ffmpeg 差分:PSNR / SSIM / 平均亮度差 tools/capture.mjs 对拍矩阵:壁纸 × 分辨率 × (正常, 藏 canvas) 实测:xilian 1920x1080 spine 层贡献 mean|diff|=26.1/255,kv37 为 10.2/255, 同配置两次截图噪声底 0.68/255。
71 lines
2.8 KiB
JavaScript
71 lines
2.8 KiB
JavaScript
// 对拍矩阵:每套壁纸 × 每种分辨率 各出两张 PNG —— 正常渲染 + 藏起 canvas 的"只有背景"参考帧,
|
|
// 然后给出"spine 层占了多少像素"的数值。
|
|
// 用法:node tools/capture.mjs <tag> [--presets xilian,kv37] [--sizes 3440x1440,1920x1080,1080x1920]
|
|
// [--base http://127.0.0.1:8181] [--propsAt load|dom] [--no-ref]
|
|
|
|
import { spawn } from "node:child_process";
|
|
import { mkdir } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
|
|
const argv = process.argv.slice(2);
|
|
const tag = argv[0];
|
|
if (!tag || tag.startsWith("--")) {
|
|
console.error("usage: node tools/capture.mjs <tag> [--presets a,b] [--sizes WxH,...] [--base url] [--propsAt dom] [--no-ref]");
|
|
process.exit(2);
|
|
}
|
|
const flag = (name, dflt) => {
|
|
const i = argv.indexOf(`--${name}`);
|
|
return i >= 0 ? argv[i + 1] : dflt;
|
|
};
|
|
const presets = flag("presets", "xilian,kv37").split(",").filter(Boolean);
|
|
const sizes = flag("sizes", "3440x1440,1920x1080,1080x1920").split(",").filter(Boolean);
|
|
const base = flag("base", "http://127.0.0.1:8181");
|
|
const propsAt = flag("propsAt", "load");
|
|
const wantRef = !argv.includes("--no-ref");
|
|
const wait = flag("wait", "25000");
|
|
|
|
const outDir = resolve("tools/shots", tag);
|
|
await mkdir(outDir, { recursive: true });
|
|
|
|
function run(cmd, args) {
|
|
return new Promise((resolve) => {
|
|
const p = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
let out = "";
|
|
p.stdout.on("data", (d) => (out += d));
|
|
p.stderr.on("data", (d) => (out += d));
|
|
p.on("close", (code) => resolve({ code, out }));
|
|
});
|
|
}
|
|
|
|
const url = (extra) => `${base}/index.html?${extra}`;
|
|
const props = (preset) => `__props=${encodeURIComponent(JSON.stringify({ preset }))}`;
|
|
|
|
const rows = [];
|
|
for (const preset of presets) {
|
|
for (const size of sizes) {
|
|
const [w, h] = size.split("x");
|
|
const main = resolve(outDir, `${preset}-${size}.png`);
|
|
const q = `${props(preset)}&__freeze=1${propsAt === "dom" ? "&__propsAt=dom" : ""}`;
|
|
const a = await run(process.execPath, ["tools/shot.mjs", main, url(q), w, h, wait]);
|
|
if (a.code !== 0) {
|
|
console.log(`FAIL ${preset} ${size}: ${a.out.trim().split("\n").pop()}`);
|
|
continue;
|
|
}
|
|
let refLine = "";
|
|
if (wantRef) {
|
|
const ref = resolve(outDir, `${preset}-${size}.ref.png`);
|
|
const b = await run(process.execPath, ["tools/shot.mjs", ref, url(q + "&__hide=canvas"), w, h, wait]);
|
|
if (b.code === 0) {
|
|
const d = await run(process.execPath, ["tools/diff.mjs", ref, main]);
|
|
refLine = d.out.trim();
|
|
} else {
|
|
refLine = "ref FAILED";
|
|
}
|
|
}
|
|
rows.push(`${preset.padEnd(7)} ${size.padEnd(11)} spine 层贡献: ${refLine}`);
|
|
console.log(rows[rows.length - 1]);
|
|
}
|
|
}
|
|
|
|
console.log(`\n输出目录: tools/shots/${tag}/`);
|