probe-v2-panel-style.mjs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. // 票 15 取证(三):填充类型分布、Panel 节点全量样式(底色/边框/圆角/阴影)、渐变停靠点
  2. import { readFileSync } from 'node:fs'
  3. const dir = '.scratch/lanhu-cockpit-asset-replacement/research/raw-v2'
  4. const boards = [2, 3, 4, 5, 6, 7, 8, 9, 10]
  5. const hex = (c) => `#${[c.r, c.g, c.b].map((v) => Math.round(v * 255).toString(16).padStart(2, '0')).join('')}`
  6. const alpha = (c) => (c.a === undefined || c.a > 0.999 ? '' : `@${c.a.toFixed(2)}`)
  7. const load = (n) => JSON.parse(readFileSync(`${dir}/desktop-${String(n).padStart(2, '0')}.json`, 'utf8')).artboard
  8. const fillTypes = new Map()
  9. const gradients = new Map()
  10. const effects = new Map()
  11. const panelNodes = []
  12. for (const n of boards) {
  13. const ab = load(n)
  14. ;(function walk(node, path = '') {
  15. const p = path ? `${path}/${node.name}` : node.name
  16. for (const f of node.style?.fills || []) {
  17. fillTypes.set(f.type, (fillTypes.get(f.type) || 0) + 1)
  18. if (f.type === 'gradient') {
  19. const stops = (f.stops || f.gradientStops || []).map((s) => `${hex(s.color)}${alpha(s.color)}@${Math.round((s.position ?? 0) * 100)}%`).join(' → ')
  20. const key = `${f.gradientType || f.type}: ${stops}`
  21. gradients.set(key, (gradients.get(key) || 0) + 1)
  22. }
  23. }
  24. for (const s of node.style?.shadows || []) {
  25. const k = `${s.type || 'shadow'} ${s.color ? hex(s.color) + alpha(s.color) : '-'} blur${s.blur ?? s.radius ?? ''} x${s.offsetX ?? s.x ?? ''} y${s.offsetY ?? s.y ?? ''}`
  26. effects.set(k, (effects.get(k) || 0) + 1)
  27. }
  28. for (const b of node.style?.blurs || []) {
  29. const k = `blur ${b.type || ''} r${b.radius ?? ''}`
  30. effects.set(k, (effects.get(k) || 0) + 1)
  31. }
  32. if (/^Panel/.test(node.name) && node.frame?.width >= 300) {
  33. panelNodes.push({ board: ab.name, path: p, w: Math.round(node.frame.width), h: Math.round(node.frame.height), fills: (node.style?.fills || []).map((f) => `${f.type}:${f.type === 'color' ? hex(f.color) + alpha(f.color) : JSON.stringify(f).slice(0, 80)}`), borders: (node.style?.borders || []).map((b) => `${b.color ? hex(b.color) + alpha(b.color) : '-'} w${b.width ?? ''} ${b.position ?? ''}`), radius: node.radius, opacity: node.style?.opacity })
  34. }
  35. for (const c of node.layers || []) walk(c, p)
  36. })(ab, '')
  37. }
  38. console.log('=== 填充类型:', [...fillTypes.entries()].map(([k, v]) => `${k}×${v}`).join(' | '))
  39. console.log('\n=== 渐变 Top 20')
  40. console.log([...gradients.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20).map(([k, v]) => `${k} ×${v}`).join('\n') || '(无)')
  41. console.log('\n=== 阴影/模糊 Top 15')
  42. console.log([...effects.entries()].sort((a, b) => b[1] - a[1]).slice(0, 15).map(([k, v]) => `${k} ×${v}`).join('\n') || '(无)')
  43. console.log('\n=== Panel 节点(≥300 宽)前 12')
  44. for (const p of panelNodes.slice(0, 12)) {
  45. console.log(`[${p.board}] ${p.path} ${p.w}x${p.h} opacity=${p.opacity}`)
  46. console.log(` fills: ${p.fills.join(' ; ')}`)
  47. console.log(` borders: ${p.borders.join(' ; ') || '(无)'} radius=${JSON.stringify(p.radius)}`)
  48. }
  49. console.log(`\n共 ${panelNodes.length} 个 Panel 节点`)