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:
2026-08-23 12:50:18 +08:00
parent 607624695d
commit c52ecffcd9
20 changed files with 904 additions and 6 deletions
+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 60"><g transform="translate(-20,-4) scale(0.9)" fill="#cbb35f" opacity="0.5">
<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><g transform="translate(180,6) scale(1.05)" fill="#cbb35f" opacity="0.35">
<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><g transform="translate(380,-4) scale(1.2)" fill="#cbb35f" opacity="0.5">
<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><g transform="translate(580,6) scale(0.9)" fill="#cbb35f" opacity="0.35">
<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><g transform="translate(780,-4) scale(1.05)" fill="#cbb35f" opacity="0.5">
<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><g transform="translate(980,6) scale(1.2)" fill="#cbb35f" opacity="0.35">
<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></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+8
View File
@@ -0,0 +1,8 @@
<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>

After

Width:  |  Height:  |  Size: 376 B

@@ -0,0 +1,31 @@
<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)"/>
<polygon points="-40,470 40,449 120,436 200,412 280,367 360,374 440,424 520,446 600,453 680,465 760,429 840,375 920,376 1000,401 1080,414 1160,450 1240,479 1300,760 -40,760" fill="#8a6d2f" opacity="0.1" />
<polygon points="-40,537 40,518 120,497 200,447 280,440 360,497 440,529 520,535 600,549 680,521 760,455 840,445 920,476 1000,492 1080,526 1160,566 1240,536 1300,760 -40,760" fill="#8a6d2f" opacity="0.15" />
<polygon points="-40,601 40,574 120,534 200,550 280,602 360,619 440,624 520,631 600,590 680,539 760,549 840,576 920,590 1000,626 1080,648 1160,604 1240,562 1300,760 -40,760" fill="#8a6d2f" opacity="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>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,7 @@
<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>

After

Width:  |  Height:  |  Size: 264 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 460"><circle cx="0" cy="0" r="0.6" fill="#f4dc9a" opacity="0.12"/><circle cx="137" cy="83" r="1.1" fill="#f4dc9a" opacity="0.3"/><circle cx="274" cy="166" r="1.6" fill="#f4dc9a" opacity="0.48"/><circle cx="411" cy="249" r="0.6" fill="#f4dc9a" opacity="0.21"/><circle cx="548" cy="332" r="1.1" fill="#f4dc9a" opacity="0.39"/><circle cx="685" cy="415" r="1.6" fill="#f4dc9a" opacity="0.12"/><circle cx="822" cy="78" r="0.6" fill="#f4dc9a" opacity="0.3"/><circle cx="959" cy="161" r="1.1" fill="#f4dc9a" opacity="0.48"/><circle cx="1096" cy="244" r="1.6" fill="#f4dc9a" opacity="0.21"/><circle cx="33" cy="327" r="0.6" fill="#f4dc9a" opacity="0.39"/><circle cx="170" cy="410" r="1.1" fill="#f4dc9a" opacity="0.12"/><circle cx="307" cy="73" r="1.6" fill="#f4dc9a" opacity="0.3"/><circle cx="444" cy="156" r="0.6" fill="#f4dc9a" opacity="0.48"/><circle cx="581" cy="239" r="1.1" fill="#f4dc9a" opacity="0.21"/><circle cx="718" cy="322" r="1.6" fill="#f4dc9a" opacity="0.39"/><circle cx="855" cy="405" r="0.6" fill="#f4dc9a" opacity="0.12"/><circle cx="992" cy="68" r="1.1" fill="#f4dc9a" opacity="0.3"/><circle cx="1129" cy="151" r="1.6" fill="#f4dc9a" opacity="0.48"/><circle cx="66" cy="234" r="0.6" fill="#f4dc9a" opacity="0.21"/><circle cx="203" cy="317" r="1.1" fill="#f4dc9a" opacity="0.39"/><circle cx="340" cy="400" r="1.6" fill="#f4dc9a" opacity="0.12"/><circle cx="477" cy="63" r="0.6" fill="#f4dc9a" opacity="0.3"/><circle cx="614" cy="146" r="1.1" fill="#f4dc9a" opacity="0.48"/><circle cx="751" cy="229" r="1.6" fill="#f4dc9a" opacity="0.21"/><circle cx="888" cy="312" r="0.6" fill="#f4dc9a" opacity="0.39"/><circle cx="1025" cy="395" r="1.1" fill="#f4dc9a" opacity="0.12"/><circle cx="1162" cy="58" r="1.6" fill="#f4dc9a" opacity="0.3"/><circle cx="99" cy="141" r="0.6" fill="#f4dc9a" opacity="0.48"/><circle cx="236" cy="224" r="1.1" fill="#f4dc9a" opacity="0.21"/><circle cx="373" cy="307" r="1.6" fill="#f4dc9a" opacity="0.39"/><circle cx="510" cy="390" r="0.6" fill="#f4dc9a" opacity="0.12"/><circle cx="647" cy="53" r="1.1" fill="#f4dc9a" opacity="0.3"/><circle cx="784" cy="136" r="1.6" fill="#f4dc9a" opacity="0.48"/><circle cx="921" cy="219" r="0.6" fill="#f4dc9a" opacity="0.21"/><circle cx="1058" cy="302" r="1.1" fill="#f4dc9a" opacity="0.39"/><circle cx="1195" cy="385" r="1.6" fill="#f4dc9a" opacity="0.12"/><circle cx="132" cy="48" r="0.6" fill="#f4dc9a" opacity="0.3"/><circle cx="269" cy="131" r="1.1" fill="#f4dc9a" opacity="0.48"/><circle cx="406" cy="214" r="1.6" fill="#f4dc9a" opacity="0.21"/><circle cx="543" cy="297" r="0.6" fill="#f4dc9a" opacity="0.39"/><circle cx="680" cy="380" r="1.1" fill="#f4dc9a" opacity="0.12"/><circle cx="817" cy="43" r="1.6" fill="#f4dc9a" opacity="0.3"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB