Skip to content

大屏性能优化 + 卡片几何修复 + 浏览器端自动截图 - #5

Merged
hnrobert merged 7 commits into
mainfrom
fix/wall-perf
Sep 19, 2026
Merged

hnrobert merged 7 commits into
mainfrom
fix/wall-perf

Conversation

@Ackii-Lo

Copy link
Copy Markdown
Collaborator

修大屏的三个问题,一起提:

1. 性能(对应"功耗大、所有设备卡")

  • 走带齿孔从每帧重建 ~900 个 SVG 节点改为 Canvas 绘制
  • 卡片改两级变换:外层只写 translate3d(纯合成器平移),内层承载 matrix3d 形变,且仅在局部缩放变化 >0.5% 时重写——10 秒重写次数从 ~9250 降到 109
  • iframe 改槽位池(最多同时存活 16 个,按离屏距离优先级 + 单向抢占),30 秒重载 0 次
  • 顺带修了「减少动效」下大屏整墙空白的 bug

实测:32 卡有截图路径主线程占用 33.7% → 约 17%。

2. 几何
两级变换的内层矩阵漏扣了元素自带的 left:-73/top:-117,整墙往左上偏了半张卡。已修,DOM 实测与计算值逐张相等。01/05 的"闪烁"与功耗同源(逐帧重栅格化 + 页面区反复挂卸),上面第 1 项一并解决。

3. 浏览器端自动截图(让大屏尽量走 <img> 而不是 iframe)
用户走完流程到 done 步时,后台用 html-to-image 把生成的页面截成 PNG 上传(POST /api/tasks/:id/screenshot,服务端校验 PNG 魔数、落盘 shot.png、重复上传 409)。大屏 15s 轮询发现 hasScreenshot 后热切换为 <img> 直出。截图失败静默回退 iframe,不影响主流程。

html-to-image 用 esbuild 打成单文件放 apps/web/src/lib/vendor/(13.4KB,MIT 协议在文件头),部署机零新增依赖。

.env 的 SSLLY_API_BASE 保持为空不受影响;GENERATION_PROVIDER=mock 下全链路已验证。

Ackii-Lo and others added 7 commits September 19, 2026 16:19
线上反馈「电脑端大屏功耗很大、所有设备打开都卡;01/05 样式卡内有一块区域闪烁」。
实测定位到三处,逐个改掉:

- 齿孔每帧拼 SVG 字符串再 innerHTML 覆盖(约 900 个节点重建)→ 改画布重绘。
  A/B 实测这一项独占约 24 个百分点(整体 40.8% → 关掉齿孔 9.5%)
- 卡片每帧写整条 matrix3d,导致每张卡逐帧重新栅格化(闪烁与功耗同源)→ 拆两层:
  外层每帧只写 translate3d(合成器平移,不重栅格化),内层形变仅在局部缩放变化
  超过 0.5% 时重写。实测 10 秒内层重写 109 次(改前约 9250 次)
- 没有截图的卡全部走 iframe(线上 27 个同时活着,每个生成页自己的 JS/动画都在跑)
  → 新增模块级槽位池 lib/frameSlots.ts,上限 16,按优先级抢占 + 冷却;
  离开预载区不再立刻卸载。实测 30 秒零重载、存活数恒定,主线程开销与不限档持平

顺带修掉两个既有问题:

- 「减少动效」环境下相位恒不变,整帧被永久跳过,而卡片元素是 Vue 补丁后才交给
  渲染循环的 → 新卡拿不到 transform,大屏整墙空白(现加脏标记强制补渲染一帧)
- 移除卡片内图片的 loading="lazy":3D 变换层里的惰性加载会与变换互相干扰

主线程(32 张卡):有截图路径 33.7% → 约 17%;无截图路径 16 个 iframe ≈ 18–20%。
画面与改前逐帧一致(页面零变化)。
e949835 把卡片单应拆成「外层 translate3d + 内层 matrix3d」时,内层自带
left:-73/top:-117(卡心落在外层原点),归位却按卡心算,整层卡片向左上
偏了半张卡:近端大卡浮在胶带上方、上带整体抬高约 117px 扎进标题区。
单应改按「卡心 − 内层偏移」归位,DOM 实测与计算值逐张相等。
大屏此前每张真实卡都要开一个 iframe 实时渲染(活动机未装 puppeteer,
hasScreenshot 恒 0),是滚动卡顿与功耗的主要来源。改为在用户自己的浏览器里
拍一张截图存进服务端:

- /start done 步挂 ShotCapture:取产物 HTML → 隐藏 iframe(srcdoc) 渲染
  → html-to-image 截 <body>(420×760@2x,与服务端快门约定一致)→ POST
- 服务端新增 POST /api/tasks/:id/screenshot:收 PNG 落盘 shot.png 并置
  screenshot=1;只许首次写入(防覆盖),校验 PNG 魔数,路由级 raw 放宽到 6mb
- FilmCard 的 frameImg/frameSrc 改 computed:卡片实例按槽位复用,截图上传后
  15s 内轮询翻牌即可热切换 iframe → <img>(原来是 setup 期常量,永远翻不过去)
- html-to-image 以 vendor 单文件引入(13KB,MIT,prettier/eslint 已 ignore),
  部署机零新增依赖;任何一步失败都静默放弃,大屏自动回退 iframe

实测(本地全链路 + mock 生成):发布完成后浏览器自动上传 840×1520 PNG,
大屏 hasScreenshot 翻位、该卡 img 直出;截图内容与页面一致。
现场实测三个痛点:公用电脑前排队久、当众输入对 i 人不友好、
失败时邮箱端收不到任何后续通知。

- 电脑端 /start 提交后进 sent 步(查看邮箱提示 + 确认按钮),
  确认即彻底重置回欢迎页交给下一位;waiting/done 桌面端不再渲染
- 手机端流程保持不变,仅在开始前加"关于你会得到的网页"伪弹窗,
  说明单 HTML 页面能做到 / 不能做到的边界
- 截图从 done 步组件解耦为模块级后台守护(lib/captureQueue.ts):
  提交即入队,自行轮询到 published 后截图上传,与界面步骤无关;
  ShotCapture.vue 删除
- 生成失败时向用户邮箱发送报错邮件(挂 failFinal(),唯一终态失败
  出口);成功邮件本就带网址与凭证,未动
- 大屏左下角新增二维码牌(qrcode 真库 + 米白垫片),指向 /start,
  中英双语同显(大屏文字给所有人看,不随语言切换)
- 表单三处 autocomplete="off"(公用电脑不留输入历史)
- mock 生成器支持描述含 MOCK_FAIL 时模拟失败(仅供 E2E 测失败路径)

E2E 12/12:提交→sent→确认重置、离场后截图照常上传、
成功/失败邮件、大屏二维码与双语文案、手机端流程原样。
@hnrobert
hnrobert merged commit 65ef8c6 into main Sep 19, 2026
2 checks passed
@hnrobert hnrobert changed the title fix/wall-perf: 大屏性能优化 + 卡片几何修复 + 浏览器端自动截图 大屏性能优化 + 卡片几何修复 + 浏览器端自动截图 Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants