// 量出一个预设在某分辨率下的取景几何,用来决定"该给什么视口"。 // 输出:作者声明的取景矩形、播放器扫描出的骨架边界、当前相机实际可见的世界矩形。 // 用法:node tools/frame.mjs import { withPage, sleep } from "./cdp.mjs"; const [port, preset, w, h] = process.argv.slice(2); if (!port || !preset || !w || !h) { console.error("usage: node tools/frame.mjs "); process.exit(2); } const EXPR = `(function () { var p = window.__player; if (!p || !p.skeleton) return { error: "player/skeleton 未就绪" }; var cv = p.currentViewport; var cam = p.sceneRenderer.camera; var cw = p.canvas.width, ch = p.canvas.height; // 用播放器自己的投影取真实可见世界矩形,不靠对 zoom 语义的猜测 var tl = cam.screenToWorld(new spine.Vector3(0, 0, 0), cw, ch); var br = cam.screenToWorld(new spine.Vector3(cw, ch, 0), cw, ch); var offset = new spine.Vector2(), size = new spine.Vector2(); p.skeleton.getBounds(offset, size, new Array(2)); var vis = { x: tl.x, y: br.y, w: br.x - tl.x, h: tl.y - br.y }; return { canvas: { w: cw, h: ch, aspect: +(cw / ch).toFixed(4) }, base: { x: +cv.x.toFixed(2), y: +cv.y.toFixed(2), w: +cv.width.toFixed(2), h: +cv.height.toFixed(2) }, pads: { l: +cv.padLeft.toFixed(2), r: +cv.padRight.toFixed(2), b: +cv.padBottom.toFixed(2), t: +cv.padTop.toFixed(2) }, resolved: { x: +(cv.x - cv.padLeft).toFixed(2), y: +(cv.y - cv.padBottom).toFixed(2), w: +(cv.width + cv.padLeft + cv.padRight).toFixed(2), h: +(cv.height + cv.padBottom + cv.padTop).toFixed(2) }, visible: { x: +vis.x.toFixed(2), y: +vis.y.toFixed(2), w: +vis.w.toFixed(2), h: +vis.h.toFixed(2), aspect: +(vis.w / vis.h).toFixed(4) }, skeletonBounds: { x: +offset.x.toFixed(2), y: +offset.y.toFixed(2), w: +size.x.toFixed(2), h: +size.y.toFixed(2) }, skeletonInView: { coversView: (offset.x <= vis.x && offset.y <= vis.y && offset.x + size.x >= vis.x + vis.w && offset.y + size.y >= vis.y + vis.h), overflowLeft: +(vis.x - offset.x).toFixed(2), overflowRight: +((offset.x + size.x) - (vis.x + vis.w)).toFixed(2), overflowTop: +((offset.y + size.y) - (vis.y + vis.h)).toFixed(2), overflowBottom: +(vis.y - offset.y).toFixed(2), }, }; })()`; const result = await withPage({ width: Number(w), height: Number(h), profile: "tools/.cache/edge-frame" }, async ({ send, evaluate }) => { const url = `http://127.0.0.1:${port}/index.html?__props=${encodeURIComponent(JSON.stringify({ preset }))}&__freeze=1`; await send("Page.navigate", { url }); for (let i = 0; i < 200; i++) { if (await evaluate("!!(window.__player && window.__player.skeleton)")) break; await sleep(150); } await sleep(400); return evaluate(EXPR); }); console.log(`${preset} @ ${w}x${h}`); console.log(JSON.stringify(result, null, 2));