fix(web): 图片预览缩放改显式 px,修 CSS zoom 被 flex max 夹回放不大 + bump 0.12.11

CSS zoom 对带 max-width/height:100% 的 flex item 无效(放大后被百分比 max
重新约束回去,视觉不变)。改为:以 scale=1 的贴合显示尺寸为基准缓存,缩放时
max:none + 显式 width/height = base × scale 像素,真正撑大布局让 body 出
滚动条;复位时清空还原自适应。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-06-15 09:42:37 +08:00
parent 31f46baaf6
commit 12171a4bdf
3 changed files with 29 additions and 5 deletions

View File

@ -21,6 +21,11 @@
## 已完成关键能力
### 2026-06-15 / 文件预览缩放改显式 px:修 CSS zoom 放不大
- 接上一条:CSS `zoom` 对带 `max-width/height:100%` 的 flex item 不生效 —— zoom 放大后被百分比 max 约束重新夹回,视觉无变化(用户实测"还是不能放大")。
- 改法(`web/static/js/preview.js` `_applyZoom`):以 scale=1 的贴合显示尺寸(`clientWidth/Height`)为基准缓存到 `z.baseW/baseH`,缩放时 `max-width/height:none` + 显式 `width/height = base × scale` px;复位时清空还原 CSS 自适应。显式 px 真正撑大布局,body 才出滚动条。bump 0.12.10 → 0.12.11。
### 2026-06-15 / 文件预览:修滚动穿透 + 图片 Ctrl+滚轮缩放
- 现象:web 端文件预览弹框内滚滚轮,事件冒泡到背景把对话列表也滚了(scroll chaining);且图片预览无缩放手段。

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。
__version__ = "0.12.10"
__version__ = "0.12.11"

View File

@ -68,9 +68,28 @@ let _fpCurrentRel = null;
const _zoomState = new WeakMap(); // bodyEl -> { img, scale, badge, timer }
function _applyZoom(z) {
// 用 CSS zoom(非 transform):zoom 改变布局盒尺寸,放大后 body 才会出滚动条能看溢出部分
z.img.style.zoom = z.scale === 1 ? "" : z.scale;
z.img.style.cursor = z.scale === 1 ? "zoom-in" : "zoom-out";
const img = z.img;
if (z.scale === 1) {
// 复位:还原 CSS 里的 max-width/height:100% 自适应贴合
img.style.maxWidth = "";
img.style.maxHeight = "";
img.style.width = "";
img.style.height = "";
img.style.cursor = "zoom-in";
} else {
// 以 scale=1 时的"贴合显示"尺寸为基准,给显式像素尺寸。
// 不用 CSS zoom:图片是带 max-width/height:100% 的 flex item,zoom 放大后会被
// 百分比 max 约束重新夹回 → 视觉不变;显式 px + max:none 才真正撑大并让 body 出滚动条。
if (!z.baseW) {
z.baseW = img.clientWidth || img.naturalWidth;
z.baseH = img.clientHeight || img.naturalHeight;
}
img.style.maxWidth = "none";
img.style.maxHeight = "none";
img.style.width = Math.round(z.baseW * z.scale) + "px";
img.style.height = Math.round(z.baseH * z.scale) + "px";
img.style.cursor = "zoom-out";
}
z.badge.textContent = Math.round(z.scale * 100) + "%";
z.badge.classList.add("show");
if (z.timer) clearTimeout(z.timer);
@ -84,7 +103,7 @@ function _makeImageZoomable(bodyEl, img) {
const badge = document.createElement("div");
badge.className = "zoom-badge"; // 挂到 card 而非 body,放大后 body 滚动时徽标不跟着滚走
card.appendChild(badge);
const z = { img, scale: 1, badge, timer: null };
const z = { img, scale: 1, badge, timer: null, baseW: 0, baseH: 0 };
_zoomState.set(bodyEl, z);
img.style.cursor = "zoom-in";
img.addEventListener("dblclick", () => { z.scale = 1; _applyZoom(z); });