现象
在 Timeline 标签处于暂停 / scrub状态时,合成帧预览没有居中铺满预览区:画面被压扁、缩小并偏到一侧(常见左/下),四周露出 --bg-preview-canvas 深色底,观感就是“黑屏 + 画面缩在左下角”。无论 seek 到哪一帧,只要暂停就复现。“黑屏”和“缩在左下角”是同一个 CSS 布局缺陷的两种表现。
复现
- 时间线放入任意素材,切到 Timeline 预览标签。
- 暂停(或拖动 scrub bar 后松手),触发
composite_frame 贴 GPU 合成帧。
- 把预览面板调成比项目画幅更高/更窄(例如把右侧时间线拉高、或窗口偏竖),即见合成帧变小、偏位、四周黑边。预览面板恰好 ≈16:9 时反而正常——这是判断依据。
根因
纯前端 CSS 布局问题,位于 web/src/components/preview/Preview.tsx 的 timelineFrameUrl 分支外层包裹 div(约 L168-186)。该 box 同时设置了三条互相冲突的样式:
-
aspectRatio: \${timeline.width} / ${timeline.height}`` (L170)
-
height: "100%" (L171)
-
maxWidth/maxHeight: "100%" (L172-173)
CSS 中显式 height 优先级高于 aspect-ratio:height:100% 撑满容器高度后,aspect-ratio 只反推 width;当该 width 超过容器宽度时 maxWidth:100% 把它截断,而 height 不会回缩 → box 的实际宽高比退化成容器的宽高比,aspect-ratio 失效。内层 <img width:100% height:100% objectFit:contain> (L184) 在这个“形状已错”的 box 里再做一次 contain,于是合成帧被二次 letterbox、缩小、偏置。
实测复现(Vite dev server 注入同款结构测量):容器 640×480 + 项目 1920×1080 → box 实测 624×464、boxAspect=1.34(应为 1.78);容器 300×400 → boxAspect=0.74。
已排除后端/合成/坐标系:src-tauri/src/render.rs 的 preview_render_size 按项目比例等比缩放(最长边封顶 1280),DTO rename_all=camelCase 与前端字段一致;crates/opentake-render/src/plan/affine.rs 单测 identity_transform_full_canvas_centered 证明默认 transform 满画幅居中;gpu/shader.wgsl (L259-272) NDC y-up 与 wgpu 一致、仅在 UV v=1-v 翻转一次,无 Y 轴翻转把画面画到左下象限的问题。
位置
web/src/components/preview/Preview.tsx:168-186(合成帧分支,主因)
web/src/components/preview/Preview.tsx:189-205(空状态占位 box 同样写法,次要,可一并评估)
建议修复
让 <img> 自身承担全部 aspect-fit,去掉与 aspect-ratio 冲突的 height:100%。最稳方案(已在 dev server 跨纵向/横向/宽扁/窄高所有容器验证:内容恒为正确比例、恒居中 centerOff=x0,y0、可放大可缩小、各朝向都只填满一个维度):
) : timelineFrameUrl ? (
<img
src={timelineFrameUrl}
alt=""
draggable={false}
style={{ width: "100%", height: "100%", objectFit: "contain", display: "block" }}
/>
) : (
由父级 flex(已 alignItems/justifyContent: center)居中,objectFit:contain 负责按比例缩放 + letterbox。
备注:若想保留画幅边框/底色,外层 box 只能用 aspectRatio + maxWidth + maxHeight(绝不要再写 height:100% 或 width:100%)。但任何“单一显式维度”的写法在相反朝向容器下都会再破比例(实测 width:100%+height:auto 在宽扁容器下 boxAspect 冲到 5.35)。因此首选上面 img 满 stage 方案。
验收
现象
在 Timeline 标签处于暂停 / scrub状态时,合成帧预览没有居中铺满预览区:画面被压扁、缩小并偏到一侧(常见左/下),四周露出
--bg-preview-canvas深色底,观感就是“黑屏 + 画面缩在左下角”。无论 seek 到哪一帧,只要暂停就复现。“黑屏”和“缩在左下角”是同一个 CSS 布局缺陷的两种表现。复现
composite_frame贴 GPU 合成帧。根因
纯前端 CSS 布局问题,位于
web/src/components/preview/Preview.tsx的timelineFrameUrl分支外层包裹div(约 L168-186)。该 box 同时设置了三条互相冲突的样式:aspectRatio: \${timeline.width} / ${timeline.height}`` (L170)height: "100%"(L171)maxWidth/maxHeight: "100%"(L172-173)CSS 中显式
height优先级高于aspect-ratio:height:100%撑满容器高度后,aspect-ratio只反推width;当该 width 超过容器宽度时maxWidth:100%把它截断,而height不会回缩 → box 的实际宽高比退化成容器的宽高比,aspect-ratio失效。内层<img width:100% height:100% objectFit:contain>(L184) 在这个“形状已错”的 box 里再做一次 contain,于是合成帧被二次 letterbox、缩小、偏置。实测复现(Vite dev server 注入同款结构测量):容器 640×480 + 项目 1920×1080 → box 实测 624×464、boxAspect=1.34(应为 1.78);容器 300×400 → boxAspect=0.74。
已排除后端/合成/坐标系:
src-tauri/src/render.rs的preview_render_size按项目比例等比缩放(最长边封顶 1280),DTOrename_all=camelCase与前端字段一致;crates/opentake-render/src/plan/affine.rs单测identity_transform_full_canvas_centered证明默认 transform 满画幅居中;gpu/shader.wgsl(L259-272) NDC y-up 与 wgpu 一致、仅在 UVv=1-v翻转一次,无 Y 轴翻转把画面画到左下象限的问题。位置
web/src/components/preview/Preview.tsx:168-186(合成帧分支,主因)web/src/components/preview/Preview.tsx:189-205(空状态占位 box 同样写法,次要,可一并评估)建议修复
让
<img>自身承担全部 aspect-fit,去掉与aspect-ratio冲突的height:100%。最稳方案(已在 dev server 跨纵向/横向/宽扁/窄高所有容器验证:内容恒为正确比例、恒居中centerOff=x0,y0、可放大可缩小、各朝向都只填满一个维度):由父级 flex(已
alignItems/justifyContent: center)居中,objectFit:contain负责按比例缩放 + letterbox。验收
<TimelinePlayback>、单素材预览切换时布局一致、无跳变。