fix: 初始化竞态、用户级覆盖语义与死代码(重构四个控制器)
初始化竞态(已实证):旧 changeConfig() 整个函数体都在 if (this.player) 里,含配置赋值, WE 在 window.load 之前下发属性时被静默丢弃,导致背景/音乐已切到新壁纸、立绘还是旧的。 改为「状态先写、应用后置」:index.js 用单一 state 记录(presetId/audioSource/audioVolume), render() 决定能否立刻应用;setConfig() 无条件记录配置,create() 用记下的配置创建。 - 删除 index.js 里重复的 wallpaperRegisterListener(该 API 在本机 WE 不存在) - 删除 adjustCanvas():播放器每帧自己做 renderer.resize(1) 且已注册 resize 监听, 而它用 getBoundingClientRect() 写 canvas.width 却不乘 DPR,属于有害的重复实现 - 用户级音源/音量覆盖:切壁纸不再清回预设默认值(旧 PresetController 的启发式已移除) - 同源设置改为 no-op:否则两档壁纸共用同一首曲子时一切换就从头播放 - 只在壁纸真的变了才重建播放器,否则拖动音量滑块会触发重新下载 3.6MB 骨架+贴图 - 未知 preset id 保持现状并告警,不再静默回退 验证:属性在 DOMContentLoaded 下发时玩家动画正确为 animation(kv37); 用户自定义音源 custom-track.mp3 与音量 0.2 在切壁纸后完整保留; 重发同一预设时音乐从 1.3s 连续到 1.9s 未重启;未知 id 不崩。 对拍:与重构前同强冻结设置 8 批次 6 配置逐像素完全相同(差异 0)。
This commit is contained in:
1 parent
a391021041
commit
4662856963
8 files changed
+1209
-240
No files matched your search
+29
-19
@@ -1,12 +1,19 @@
|
||||
// 两张 PNG 的数值对拍:PSNR / SSIM / 平均亮度差(0-255)。
|
||||
// 用法:node tools/diff.mjs <a.png> <b.png> [diff-out.png]
|
||||
// 判读:平均差 < 1 视为肉眼无差异;1-3 为细微;> 8 通常意味着构图/内容变了。
|
||||
// 两张图的数值对拍:PSNR / SSIM / 平均绝对差 / 最大绝对差(均为 0-255 尺度)。
|
||||
// 用法:node tools/diff.mjs <a> <b> [diff-out.png]
|
||||
//
|
||||
// 判读:
|
||||
// mean|diff| ≈ 0 且 max|diff| = 0 → 逐像素相同
|
||||
// mean|diff| 极小 但 max|diff| 很大 → 只有零星像素变了(多半是高对比边缘的抗锯齿抖动)
|
||||
// mean|diff| 明显变大 → 整体变化(构图/内容/色调)
|
||||
//
|
||||
// 注意:ffmpeg 对很小的数值会用科学计数法(YAVG=4.96721e-05),
|
||||
// 解析时必须带上指数,否则会把 0.00005 误读成 4.97——这个坑踩过一次。
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
const [a, b, outPng] = process.argv.slice(2);
|
||||
if (!a || !b) {
|
||||
console.error("usage: node tools/diff.mjs <a.png> <b.png> [diff-out.png]");
|
||||
console.error("usage: node tools/diff.mjs <a> <b> [diff-out.png]");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@@ -20,28 +27,31 @@ function ff(args) {
|
||||
});
|
||||
}
|
||||
|
||||
const psnr = await ff(["-i", a, "-i", b, "-lavfi", "psnr", "-f", "null", "-"]);
|
||||
const ssim = await ff(["-i", a, "-i", b, "-lavfi", "ssim", "-f", "null", "-"]);
|
||||
const yavg = await ff([
|
||||
"-i", a, "-i", b,
|
||||
"-lavfi", "blend=all_mode=difference,signalstats,metadata=print:key=lavfi.signalstats.YAVG",
|
||||
"-f", "null", "-",
|
||||
]);
|
||||
|
||||
const NUM = "([-+0-9.eE]+)";
|
||||
const num = (text, re) => {
|
||||
const m = text.match(re);
|
||||
return m ? Number(m[1]) : NaN;
|
||||
};
|
||||
|
||||
const yavgs = [...yavg.matchAll(/YAVG=([\d.]+)/g)].map((m) => Number(m[1]));
|
||||
const psnr = await ff(["-i", a, "-i", b, "-lavfi", "psnr", "-f", "null", "-"]);
|
||||
const ssim = await ff(["-i", a, "-i", b, "-lavfi", "ssim", "-f", "null", "-"]);
|
||||
const stats = await ff([
|
||||
"-i", a, "-i", b,
|
||||
"-lavfi", "blend=all_mode=difference,format=gray,signalstats,metadata=print",
|
||||
"-f", "null", "-",
|
||||
]);
|
||||
|
||||
const report = {
|
||||
psnr_avg: /average:inf/.test(psnr) ? Infinity : num(psnr, /average:([\d.]+)/),
|
||||
ssim_all: num(ssim, /All:([\d.]+)/),
|
||||
mean_abs_diff: yavgs.length ? yavgs[yavgs.length - 1] : NaN,
|
||||
};
|
||||
const frames = [...stats.matchAll(/frame:(\d+)/g)].length;
|
||||
const yavg = num(stats, new RegExp(`YAVG=${NUM}`));
|
||||
const ymax = num(stats, new RegExp(`YMAX=${NUM}`));
|
||||
const inf = /average:inf/.test(psnr);
|
||||
|
||||
console.log(`psnr=${report.psnr_avg} dB ssim=${report.ssim_all} mean|diff|=${report.mean_abs_diff}/255`);
|
||||
console.log(
|
||||
`psnr=${inf ? "∞" : num(psnr, new RegExp(`average:${NUM}`))} dB ` +
|
||||
`ssim=${num(ssim, new RegExp(`All:${NUM}`))} ` +
|
||||
`mean|diff|=${yavg}/255 max|diff|=${ymax}` +
|
||||
(frames > 1 ? ` [${frames} 帧,取首帧]` : ""),
|
||||
);
|
||||
|
||||
if (outPng) {
|
||||
await ff(["-y", "-i", a, "-i", b, "-lavfi", "blend=all_mode=difference", "-frames:v", "1", outPng]);
|
||||
|
||||
+8
-1
@@ -88,6 +88,8 @@ const DRIVER = (q) => `<script>
|
||||
// 保证"同一份代码的任何两次截图"是同一相位,对拍才有意义。
|
||||
if (freeze) {
|
||||
try {
|
||||
this.paused = true;
|
||||
this.speed = 0;
|
||||
var cur = this.animationState && this.animationState.getCurrent(0);
|
||||
if (cur) cur.trackTime = 0;
|
||||
if (this.skeleton) this.skeleton.updateWorldTransform(2);
|
||||
@@ -100,7 +102,12 @@ const DRIVER = (q) => `<script>
|
||||
|
||||
function freezePlayer(p) {
|
||||
try {
|
||||
p.pause();
|
||||
// 冻结姿势。关键在 paused=true:drawFrame 里 playDelta = paused ? 0 : delta * speed,
|
||||
// 于是 animationState.update(0) 不会推进相位。只把 trackTime 置 0 是不够的——
|
||||
// 那是在播放器 update(playDelta) 之前设的,渲染出来的仍是 t=playDelta 的姿势,
|
||||
// 而 playDelta 随帧时长浮动,会留下"边缘亚像素抖动"式的伪差异。
|
||||
p.paused = true;
|
||||
p.speed = 0;
|
||||
var cur = p.animationState && p.animationState.getCurrent(0);
|
||||
if (cur) cur.trackTime = 0;
|
||||
if (p.skeleton) p.skeleton.updateWorldTransform(2);
|
||||
|
||||
Reference in new issue
Block a user