fix: traverse all nodes, use slide container size for canvas, extract slide bg color
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* web-to-pixso JSON → 翻译引擎 Schema 转换器
|
||||
*
|
||||
* 把 web-to-pixso Chrome 扩展导出的 JSON 转成 renderer/index.js 能吃的标准格式
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
function convert(inputPath, outputPath) {
|
||||
const raw = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
|
||||
const nodes = raw.nodes;
|
||||
const canvas = raw.canvas || {};
|
||||
|
||||
// 画布尺寸
|
||||
const canvasW = canvas.width || 1920;
|
||||
const canvasH = canvas.height || 1080;
|
||||
|
||||
// 收集所有页面(node.children 包含多页时按页面分组)
|
||||
const pages = [];
|
||||
|
||||
function extractPage(node, parentY) {
|
||||
if (!node || !node.rect) return null;
|
||||
|
||||
const rect = node.rect;
|
||||
const styles = node.styles || {};
|
||||
const layout = node.layout || {};
|
||||
const children = node.children || [];
|
||||
|
||||
// 跳过不可见节点
|
||||
if (styles.display === 'none' || styles.visibility === 'hidden') return null;
|
||||
|
||||
const elements = [];
|
||||
|
||||
// 如果是文字节点
|
||||
if (node.type === 'text' && node.src) {
|
||||
elements.push({
|
||||
type: 'text',
|
||||
text: node.src,
|
||||
options: {
|
||||
x: rect.x / 96,
|
||||
y: (rect.y - parentY) / 96,
|
||||
w: rect.w / 96,
|
||||
h: rect.h / 96,
|
||||
fontSize: styles.fontSize || 14,
|
||||
fontFace: (styles.fontFamily || 'Arial').split(',')[0].replace(/['"]/g, '').trim(),
|
||||
color: (styles.color || '#000000').replace('#', ''),
|
||||
bold: styles.fontWeight >= 700 || styles.fontWeight === 'bold',
|
||||
italic: styles.fontStyle === 'italic',
|
||||
align: styles.textAlign || 'left'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 如果有背景色或背景图,生成 shape
|
||||
if (styles.backgroundColor && styles.backgroundColor !== 'rgba(0,0,0,0)' && styles.backgroundColor !== 'transparent') {
|
||||
elements.push({
|
||||
type: 'shape',
|
||||
shapeName: 'rect',
|
||||
options: {
|
||||
x: rect.x / 96,
|
||||
y: (rect.y - parentY) / 96,
|
||||
w: rect.w / 96,
|
||||
h: rect.h / 96,
|
||||
fill: { color: styles.backgroundColor.replace('#', '') }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理子节点
|
||||
for (const childId of children) {
|
||||
const childNode = nodes[childId];
|
||||
if (childNode) {
|
||||
const childElements = extractPage(childNode, parentY || rect.y);
|
||||
if (childElements) elements.push(...childElements);
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
// 找顶层容器
|
||||
const root = nodes.children ? findRoot(nodes) : null;
|
||||
if (root) {
|
||||
const pageElements = extractPage(root, 0);
|
||||
if (pageElements && pageElements.length > 0) {
|
||||
pages.push({ objects: pageElements });
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有分页,把整个 canvas 当一页
|
||||
if (pages.length === 0) {
|
||||
const allElements = [];
|
||||
for (const [id, node] of Object.entries(nodes)) {
|
||||
if (node.rect && node.type) {
|
||||
const elems = extractPage(node, 0);
|
||||
if (elems) allElements.push(...elems);
|
||||
}
|
||||
}
|
||||
pages.push({ objects: allElements });
|
||||
}
|
||||
|
||||
// 输出 Schema
|
||||
const spec = {
|
||||
presentation: { layout: 'LAYOUT_16x9' },
|
||||
slides: pages
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2));
|
||||
console.log(`Converted: ${inputPath} → ${outputPath}`);
|
||||
console.log(`Pages: ${pages.length}, Total objects: ${pages.reduce((s,p) => s + p.objects.length, 0)}`);
|
||||
}
|
||||
|
||||
function findRoot(nodes) {
|
||||
// 找没有 parent 引用的节点作为根
|
||||
const allIds = new Set(Object.keys(nodes));
|
||||
const childIds = new Set();
|
||||
for (const [id, node] of Object.entries(nodes)) {
|
||||
if (node.children) {
|
||||
for (const cid of node.children) childIds.add(cid);
|
||||
}
|
||||
}
|
||||
for (const id of allIds) {
|
||||
if (!childIds.has(id)) return nodes[id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// CLI
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 1) {
|
||||
console.error('Usage: node convert-w2p.js <input.json> [output.json]');
|
||||
process.exit(1);
|
||||
}
|
||||
const inputPath = args[0];
|
||||
const outputPath = args[1] || inputPath.replace('.json', '_schema.json');
|
||||
convert(inputPath, outputPath);
|
||||
Reference in New Issue
Block a user