v0.1.13: 金碧山河(SVG/Canvas 素材 + 动效系统 + 舞美重装)
【素材生成(零外部资源)】 - scripts/gen-art.mjs 程序化产出 5 枚 SVG 资产:金碧山水全景(山脊/金乌/雁阵/水纹)、 祥云纹带、回纹角饰、仙鹤、星辰层 → src/renderer/assets/gen/,npm run gen:art 可重跑 【Canvas 动效系统(单 RAF)】 - ui/fx-canvas.ts:灵雾粒子流(常驻随季色)/金砂喷发(spark)/刀光(blade)/涟漪(ripple), 单实例防重入、防膨胀 cap、GPU 友好、visibility 自停 - ui/fx.ts + core/fxqueue.ts:FxGate 统一发射闸——档位过滤(柔/标/全)、 reduced-motion 降级、单帧批处理防竞态、deltas 纯函数(漂字差分) 【舞美】 - 金碧背景层 bg-scene(全页面,随季节 hue-rotate 色温渐变) - 顶栏时辰司:季节印(春·熏风/夏·蝉鸣/秋·霜叶/冬·雪静)+年月进度环(SVG 弧段)+节气名 - 成员卡:闭关 aura 三点浮动 + 修为流光 shimmer - 消息流:good 事件(突破/获宝/落槌)→金砂喷发,战事→刀光扫过战报 - Boot 卷轴展开过渡;drift 漂字层(独立节点队列不覆盖,8 条裁剪) - 设置页动效三档(柔和/标准/全开) 【竞态规避】 - RAF 单循环 guard、particles cap 裁剪、FxGate draining 互斥、漂字节点附加不覆盖、 visibility 自动降级、advance in-flight 闸沿用 【测试】937→967(timesense 矩阵 15 + FxGate 竞态/档位/deltas 15) 全量 35 套件通过;金钟罩零漂移(纯 UI/素材层);typecheck/build 通过
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* gen-art.mjs — 程序化生成 SVG 素材(金碧山水/纹样/饰件),零外部资源。
|
||||
* 用法:node scripts/gen-art.mjs (产物写 src/renderer/assets/gen/*.svg)
|
||||
*/
|
||||
import { mkdirSync, writeFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const OUT = join(dirname(fileURLToPath(import.meta.url)), '../src/renderer/assets/gen')
|
||||
mkdirSync(OUT, { recursive: true })
|
||||
|
||||
// ============ 1. 金碧山水全景(主背景) ============
|
||||
function goldenLandscape() {
|
||||
const ridge = (y, amp, seed, opacity) => {
|
||||
const pts = []
|
||||
let x = -40
|
||||
let phase = seed
|
||||
while (x <= 1280) {
|
||||
const yy = y + Math.sin((x / 170) + phase) * amp + Math.sin((x / 71) + phase * 2.2) * amp * 0.3
|
||||
pts.push(`${x.toFixed(0)},${yy.toFixed(0)}`)
|
||||
phase += 0.35
|
||||
x += 80
|
||||
}
|
||||
return `<polygon points="${pts.join(' ')} 1300,760 -40,760" fill="#8a6d2f" opacity="${opacity}" />`
|
||||
}
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 760" preserveAspectRatio="xMidYMax slice">
|
||||
<defs>
|
||||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#181208"/>
|
||||
<stop offset="0.65" stop-color="#241a10"/>
|
||||
<stop offset="1" stop-color="#38271a"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="sun" cx="0.5" cy="0.5" r="0.5">
|
||||
<stop offset="0" stop-color="#f2cf7e" stop-opacity="0.9"/>
|
||||
<stop offset="0.5" stop-color="#c99a3d" stop-opacity="0.35"/>
|
||||
<stop offset="1" stop-color="#c99a3d" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<filter id="blur2"><feGaussianBlur stdDeviation="2"/></filter>
|
||||
<filter id="blur6"><feGaussianBlur stdDeviation="6"/></filter>
|
||||
</defs>
|
||||
<rect width="1200" height="760" fill="url(#sky)"/>
|
||||
<circle cx="905" cy="150" r="130" fill="url(#sun)"/>
|
||||
<circle cx="905" cy="150" r="30" fill="#f6dd9d" opacity="0.75" filter="url(#blur2)"/>
|
||||
${ridge(420, 46, 1.4, 0.10)}
|
||||
${ridge(500, 52, 2.1, 0.15)}
|
||||
${ridge(590, 46, 3.2, 0.22)}
|
||||
<g stroke="#cbb35f" stroke-width="1.6" opacity="0.35" fill="none" filter="url(#blur2)">
|
||||
<path d="M300 262 q16 -11 32 0 q16 -11 32 0"/>
|
||||
<path d="M250 288 q14 -10 28 0 q14 -10 28 0"/>
|
||||
<path d="M980 205 q13 -9 26 0 q13 -9 26 0"/>
|
||||
</g>
|
||||
<g stroke="#cbb35f" stroke-width="0.9" opacity="0.22">
|
||||
<path d="M60 620 h300 M60 640 h220 M60 660 h150" />
|
||||
</g>
|
||||
<rect x="0" y="700" width="1200" height="60" fill="#1c1409" opacity="0.4"/>
|
||||
</svg>`
|
||||
}
|
||||
|
||||
// ============ 2. 祥云纹带(分隔/装饰) ============
|
||||
function cloudBand() {
|
||||
const cloud = (x, y, s, o) =>
|
||||
`<g transform="translate(${x},${y}) scale(${s})" fill="#cbb35f" opacity="${o}">
|
||||
<path d="M0 0 q14 -20 34 -18 q26 2 30 14 q18 -4 26 8 q4 12 -8 16 H8 q-14 -2 -8 -20z"/>
|
||||
</g>`
|
||||
let out = ''
|
||||
for (let i = 0; i < 6; i++) out += cloud(i * 200 - 20, ((i % 2) * 10) - 4, 0.9 + (i % 3) * 0.15, 0.5 - (i % 2) * 0.15)
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 60">${out}</svg>`
|
||||
}
|
||||
|
||||
// ============ 3. 回纹角饰(卡片角框) ============
|
||||
function meanderCorner() {
|
||||
const sq = (x, y, s) => `<path d="M${x} ${y + s} h${s} v${s} h${-s * 0} z" fill="none" stroke="#b3913e" stroke-width="2"/>`
|
||||
let p = ''
|
||||
for (let i = 0; i < 4; i++) {
|
||||
p += `<path d="M${i * 8 + 0} 0 v${i * 8 + 0} M0 ${i * 8 + 0} h${i * 8 + 0}" />`
|
||||
}
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
|
||||
<g fill="none" stroke="#b3913e" stroke-width="1.6" opacity="0.7">
|
||||
<path d="M4 36 L4 4 L36 4" />
|
||||
<path d="M10 30 L10 10 L34 10" />
|
||||
<path d="M16 24 L16 16 L30 16" />
|
||||
</g>
|
||||
</svg>`
|
||||
}
|
||||
|
||||
// ============ 4. 仙鹤(点缀) ============
|
||||
function crane() {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<g stroke="#e0cf9b" stroke-width="2.2" fill="none" opacity="0.7" stroke-linecap="round">
|
||||
<path d="M8 52 q22 -18 42 -16"/>
|
||||
<path d="M50 36 q14 12 30 14"/>
|
||||
<path d="M44 34 L22 22 M44 34 L70 20 M44 34 L52 14"/>
|
||||
</g>
|
||||
<circle cx="42" cy="33" r="2.4" fill="#e6b84c" opacity="0.9"/>
|
||||
</svg>`
|
||||
}
|
||||
|
||||
// ============ 5. 星辰点缀层 ============
|
||||
function starfield() {
|
||||
let p = ''
|
||||
for (let i = 0; i < 42; i++) {
|
||||
const x = ((i * 137) % 1200)
|
||||
const y = ((i * 83) % 420)
|
||||
const r = 0.6 + (i % 3) * 0.5
|
||||
const o = 0.12 + ((i * 7) % 5) * 0.09
|
||||
p += `<circle cx="${x}" cy="${y}" r="${r}" fill="#f4dc9a" opacity="${o}"/>`
|
||||
}
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 460">${p}</svg>`
|
||||
}
|
||||
|
||||
const out = {
|
||||
'landscape-gold.svg': goldenLandscape(),
|
||||
'cloud-band.svg': cloudBand(),
|
||||
'meander-corner.svg': meanderCorner(),
|
||||
'crane.svg': crane(),
|
||||
'starfield.svg': starfield()
|
||||
}
|
||||
|
||||
for (const [name, svg] of Object.entries(out)) {
|
||||
writeFileSync(join(OUT, name), svg, 'utf-8')
|
||||
console.log('✓', name, `${svg.length} B`)
|
||||
}
|
||||
console.log('gen-art done →', OUT)
|
||||
Reference in New Issue
Block a user