图片灯箱:正文图片点击在当前页放大、多图轮播、键盘切换 #10

Closed
opened 2026-10-25 12:31:25 +00:00 by laoyang · 2 comments
Owner

现象

当前主题(未装第三方插件时)点击文章正文里的图片,会在新标签页打开原图。

用户为此装了插件才实现「在当前页放大」。现希望主题自带该能力,不再依赖插件。

诉求

  1. 正文内所有图片都可以点击放大(这些图多数是缩略图)
  2. 点击后取出原图(full size),而不是缩略图
  3. 若开启了 CDN 加速,原图地址也走 CDN(与正文图片同一套地址规则)
  4. 在当前页面覆盖展示 —— 不跳转、不开新标签页,把页面其它内容全部盖住
  5. 背景可黑、也可非黑,需要能实际比较哪种效果好
  6. 一篇文章多张图时,左右各有箭头,可在多图之间轮播
  7. 响应键盘上下左右键切换

边界

  • 不引入任何第三方库(主题现有约定:零外部依赖、零外网请求)
  • 不破坏无 JS 时的降级行为(仍应能点开原图)

验收标准

  • 单图文章:点击 → 当前页覆盖展示原图;ESC / 点空白处 / 关闭按钮都能退出
  • 多图文章:左右箭头可轮播,键盘 ←→↑↓ 可切换,显示「第几张 / 共几张」
  • 缩略图点击取到的是原图(不是 -300x200 这类缩略尺寸)
  • 开启 CDN 后,灯箱里加载的地址是 CDN 地址
  • 关闭该功能开关后,完全退回原有行为(无新增 DOM、无新增资源加载)
  • 遮罩配色可在后台切换(深色 / 浅色),且两种都被验证过文字与控件可读
## 现象 当前主题(未装第三方插件时)点击文章正文里的图片,会在**新标签页**打开原图。 用户为此装了插件才实现「在当前页放大」。现希望主题**自带**该能力,不再依赖插件。 ## 诉求 1. **正文内所有图片**都可以点击放大(这些图多数是缩略图) 2. 点击后取出**原图**(full size),而不是缩略图 3. 若开启了 CDN 加速,原图地址也**走 CDN**(与正文图片同一套地址规则) 4. 在**当前页面**覆盖展示 —— 不跳转、不开新标签页,把页面其它内容全部盖住 5. 背景**可黑、也可非黑**,需要能实际比较哪种效果好 6. 一篇文章多张图时,左右各有箭头,可在多图之间**轮播** 7. 响应键盘**上下左右键**切换 ## 边界 - 不引入任何第三方库(主题现有约定:零外部依赖、零外网请求) - 不破坏无 JS 时的降级行为(仍应能点开原图) ## 验收标准 - 单图文章:点击 → 当前页覆盖展示原图;ESC / 点空白处 / 关闭按钮都能退出 - 多图文章:左右箭头可轮播,键盘 ←→↑↓ 可切换,显示「第几张 / 共几张」 - 缩略图点击取到的是原图(不是 `-300x200` 这类缩略尺寸) - 开启 CDN 后,灯箱里加载的地址是 CDN 地址 - 关闭该功能开关后,完全退回原有行为(无新增 DOM、无新增资源加载) - 遮罩配色可在后台切换(深色 / 浅色),且两种都被验证过文字与控件可读
Author
Owner

交付说明(3.2.40 已发布到更新通道,等实测)

1. 做了什么

主题自带灯箱,不再依赖插件。新增三个文件:

  • inc/theme-lightbox.php —— 服务端改写正文(把 <a href="缩略图"><img></a>
    改写成「href=原图 + data-yang-lb="1"」),并按后台设置注入一段行内 CSS
  • assets/css/lightbox.css —— 覆盖层、箭头、关闭按钮、底部计数
  • assets/js/lightbox.js —— 原生 JS,无 jQuery、无第三方库

functions.php 里 require,位置在 theme-article.php 之后。

2. 按您的 7 条诉求逐条落地

诉求 实现
① 正文所有图片可点 服务端给图片链接打标记;只接管「href 指向图片且内部包着 img」的 <a>
② 点开取原图 三级兜底:附件 ID → full(最可靠);img 上的 data-full/data-large-file/data-orig-file;最后才是 href
③ 开 CDN 也走 CDN 原图地址只写在 href 里(见下面第 4 条,这是硬约束)
④ 当前页覆盖 position:fixed + z-index:99999,盖住整页
⑤ 黑底 / 非黑 后台可切「白色遮罩 / 深色遮罩」,默认白色(您看过两套对比后选的)
⑥ 多图左右箭头轮播 左右两个圆形箭头,到头绕回;底部显示「第几张 / 共几张」+ 图片说明
⑦ 键盘切换 ← → 上一张/下一张,↑ ↓ 同样切换,Esc 关闭,Home/End 跳首尾

快捷键汇总:← ↑ 上一张 · → ↓ 下一张 · Esc 关闭 · 点空白处关闭 · 点右上角 × 关闭。

3. 遮罩半透明度

按您的反馈做成了可调(80% 是默认):后台「图片灯箱 → 遮罩不透明度」,
九档(100/95/90/85/80/75/70/60/50)。

这里有一个实现上的选择值得说明:改的是遮罩底色的 alpha(rgba(255,255,255,0.8)),
不是 CSS 的 opacity。用 opacity 会让整层一起变淡 —— 照片、箭头、底部文字
全都发灰,正好跟「底透、图清楚」相反。

半透明带来两个连带改动,都已处理:

  • 底部说明条的底色单独提到 0.9(原来用渐变到 transparent,遮罩一透,
    渐变那段就完全没底,说明文字直接压在正文上,深色大图时读不清)
  • 遮罩透到 50% 时,文字按**最坏背景(纯黑)**复核过对比度仍 ≥ 4.5:1

4. 一个必须记住的硬约束:原图只能放 href,不能放 data-full

主题的 CDN 是整页输出缓冲改写(inc/theme-cdn.php),它只改 src= / href=
两个属性名和 CSS 里的 url()。如果把原图地址写成 data-full="...",
CDN 完全看不见 —— 结果就是缩略图走了 CDN、点开看原图却回源站,
用户会觉得「一放大就变慢」。所以原图地址只能写在 href 里。

5. 关于「不装插件时点击跳新标签页」和插件共存

实测抽了线上 100 篇文章、112 张图:112/112 全部带着插件写进数据库的
触发属性(data-rel="lightbox-gallery-0"、data-imagelightbox、data-rl_title 等),
主题自身从不产生这些属性(全仓库 grep 0 处)。

所以本版的做法是:

  • 开关打开 → 主题接管,并剥掉这些插件触发属性(否则点一张图会同时弹出两套遮罩)
  • 开关关闭 → 正文逐字节不动,插件照常可用

剥离只发生在「确定被灯箱接管的链接」上。像
<a href="/wp-login.php"><img src="avatar.jpg"></a> 这种「图片包在普通链接里」的结构
不归灯箱管,属性原样保留。

建议升级后把旧的灯箱插件停用,避免两套交互逻辑抢同一个点击。

6. 验证

项目 结果
服务端改写探针(PHP 8.4 + 8.5 各一遍) 55 通过 / 0 失败
浏览器交互探针(深浅两配色 × 80%,另加 50% / 100% 极端档) 200 通过 / 0 失败
反向验证(故意把实现改坏,看守卫会不会报) 23/23 全部按预期捕获
包内容契约检查 96 通过 / 0 失败
演示页自检(含真点击) 34 通过 / 0 失败
线上发布脚本的三条反向注入 3/3 判据有效

实测几何(1360×800 视口,两套配色只在颜色上有差异):

覆盖层   x=0    y=0    w=1360 h=800
图片     x=66   y=131  w=1228 h=537     ← 原图 1600×700,比例 2.286 等比未变形
左箭头   x=24   y=374  w=52   h=52
右箭头   x=1284 y=374  w=52   h=52
关闭     x=1298 y=18   w=44   h=44
底部条   x=0    y=749  w=1360 h=52

白色:遮罩 rgba(255,255,255,0.8)  说明条 rgba(255,255,255,0.9)
深色:遮罩 rgba(12,14,18,0.8)     说明条 rgba(12,14,18,0.9)
覆盖层 opacity = 1   图片 opacity = 1

7. 开发过程中真的犯过、并被守卫固化的两个 bug

  1. 行内样式选择器优先级不足。行内写 .yang-lb{--lb-bg:...} 优先级 (0,1,0),
    而样式表里深色配色那条是 .yang-lb[data-theme='dark'] (0,2,0) ——
    被压掉,表现成「切到深色配色,透明度设置静默失效」,而浅色配色下看着完全正常。
    修法是选择器也带上 [data-theme='...']。已固化成反向注入 U9。

  2. data-full 兜底是死代码。yang_lightbox_img_src() 一开始定义了但没人调用,
    意味着「三处都是缩略图、只有 img 的 data-full 里是真原图、且 img 没有 wp-image 类」
    这类结构取不到原图。已接进三级兜底,并固化成反向注入 S12 / S13。

8. 发布与实测

  • 代码提交:7125fdd + 1cf4267(含上面的兜底修复),均已推送
  • 已发到更新通道 /theme-update/:update.json = 3.2.40,zip 与 readme 均 200,
    下载 md5 与磁盘一致
  • 服务器上的主题目录仍是 3.2.39,没有被覆盖 —— 请在 WP 后台点「升级」

升级后请重点试这几项:

  1. 单图文章:点一下 → 当前页盖住、图清楚、底下隐约可见;Esc / 点空白 / 点 × 都能退
  2. 多图文章:左右箭头、键盘 ←→↑↓ 都能切,底部「第几张 / 共几张」跟着变
  3. 后台把「遮罩不透明度」调到 70% / 60%,看还读不读得清(尤其深色图片多的页面)
  4. 后台切到「深色遮罩」,再调一次不透明度 —— 这是当初那个优先级 bug 的复现场景
  5. 关掉「启用图片灯箱」→ 点图应退回「新标签页打开原图」
  6. 手机上点开图片,看箭头是否够大好点、底部文字会不会被遮

哪一项不对请直接说现象,我这边按工单继续跟。等您确认没问题再关单。

## 交付说明(3.2.40 已发布到更新通道,等实测) ### 1. 做了什么 主题自带灯箱,不再依赖插件。新增三个文件: - `inc/theme-lightbox.php` —— 服务端改写正文(把 `<a href="缩略图"><img></a>` 改写成「href=原图 + `data-yang-lb="1"`」),并按后台设置注入一段行内 CSS - `assets/css/lightbox.css` —— 覆盖层、箭头、关闭按钮、底部计数 - `assets/js/lightbox.js` —— 原生 JS,无 jQuery、无第三方库 `functions.php` 里 require,位置在 `theme-article.php` 之后。 ### 2. 按您的 7 条诉求逐条落地 | 诉求 | 实现 | |---|---| | ① 正文所有图片可点 | 服务端给图片链接打标记;只接管「href 指向图片且内部包着 img」的 `<a>` | | ② 点开取**原图** | 三级兜底:附件 ID → full(最可靠);img 上的 `data-full`/`data-large-file`/`data-orig-file`;最后才是 href | | ③ 开 CDN 也走 CDN | 原图地址**只写在 `href` 里**(见下面第 4 条,这是硬约束) | | ④ 当前页覆盖 | `position:fixed` + `z-index:99999`,盖住整页 | | ⑤ 黑底 / 非黑 | 后台可切「白色遮罩 / 深色遮罩」,默认**白色**(您看过两套对比后选的) | | ⑥ 多图左右箭头轮播 | 左右两个圆形箭头,到头绕回;底部显示「第几张 / 共几张」+ 图片说明 | | ⑦ 键盘切换 | ← → 上一张/下一张,↑ ↓ 同样切换,Esc 关闭,Home/End 跳首尾 | 快捷键汇总:`←` `↑` 上一张 · `→` `↓` 下一张 · `Esc` 关闭 · 点空白处关闭 · 点右上角 × 关闭。 ### 3. 遮罩半透明度 按您的反馈做成了**可调**(80% 是默认):后台「图片灯箱 → 遮罩不透明度」, 九档(100/95/90/85/80/75/70/60/50)。 这里有一个实现上的选择值得说明:改的是**遮罩底色的 alpha**(`rgba(255,255,255,0.8)`), **不是** CSS 的 `opacity`。用 `opacity` 会让整层一起变淡 —— 照片、箭头、底部文字 全都发灰,正好跟「底透、图清楚」相反。 半透明带来两个连带改动,都已处理: - 底部说明条的底色单独提到 0.9(原来用渐变到 transparent,遮罩一透, 渐变那段就完全没底,说明文字直接压在正文上,深色大图时读不清) - 遮罩透到 50% 时,文字按**最坏背景(纯黑)**复核过对比度仍 ≥ 4.5:1 ### 4. 一个必须记住的硬约束:原图只能放 `href`,不能放 `data-full` 主题的 CDN 是**整页输出缓冲改写**(`inc/theme-cdn.php`),它只改 `src=` / `href=` 两个属性名和 CSS 里的 `url()`。如果把原图地址写成 `data-full="..."`, CDN 完全看不见 —— 结果就是缩略图走了 CDN、点开看原图却回源站, 用户会觉得「一放大就变慢」。所以原图地址只能写在 href 里。 ### 5. 关于「不装插件时点击跳新标签页」和插件共存 实测抽了线上 100 篇文章、112 张图:**112/112 全部**带着插件写进数据库的 触发属性(`data-rel="lightbox-gallery-0"`、`data-imagelightbox`、`data-rl_title` 等), 主题自身从不产生这些属性(全仓库 grep 0 处)。 所以本版的做法是: - 开关打开 → 主题接管,并**剥掉**这些插件触发属性(否则点一张图会同时弹出两套遮罩) - 开关关闭 → 正文**逐字节不动**,插件照常可用 剥离只发生在「确定被灯箱接管的链接」上。像 `<a href="/wp-login.php"><img src="avatar.jpg"></a>` 这种「图片包在普通链接里」的结构 不归灯箱管,属性原样保留。 **建议升级后把旧的灯箱插件停用**,避免两套交互逻辑抢同一个点击。 ### 6. 验证 | 项目 | 结果 | |---|---| | 服务端改写探针(PHP 8.4 + 8.5 各一遍) | **55 通过 / 0 失败** | | 浏览器交互探针(深浅两配色 × 80%,另加 50% / 100% 极端档) | **200 通过 / 0 失败** | | 反向验证(故意把实现改坏,看守卫会不会报) | **23/23 全部按预期捕获** | | 包内容契约检查 | **96 通过 / 0 失败** | | 演示页自检(含真点击) | **34 通过 / 0 失败** | | 线上发布脚本的三条反向注入 | 3/3 判据有效 | 实测几何(1360×800 视口,两套配色只在颜色上有差异): ``` 覆盖层 x=0 y=0 w=1360 h=800 图片 x=66 y=131 w=1228 h=537 ← 原图 1600×700,比例 2.286 等比未变形 左箭头 x=24 y=374 w=52 h=52 右箭头 x=1284 y=374 w=52 h=52 关闭 x=1298 y=18 w=44 h=44 底部条 x=0 y=749 w=1360 h=52 白色:遮罩 rgba(255,255,255,0.8) 说明条 rgba(255,255,255,0.9) 深色:遮罩 rgba(12,14,18,0.8) 说明条 rgba(12,14,18,0.9) 覆盖层 opacity = 1 图片 opacity = 1 ``` ### 7. 开发过程中真的犯过、并被守卫固化的两个 bug 1. **行内样式选择器优先级不足**。行内写 `.yang-lb{--lb-bg:...}` 优先级 (0,1,0), 而样式表里深色配色那条是 `.yang-lb[data-theme='dark']` (0,2,0) —— 被压掉,表现成「切到深色配色,透明度设置静默失效」,而浅色配色下看着完全正常。 修法是选择器也带上 `[data-theme='...']`。已固化成反向注入 U9。 2. **`data-full` 兜底是死代码**。`yang_lightbox_img_src()` 一开始定义了但没人调用, 意味着「三处都是缩略图、只有 img 的 data-full 里是真原图、且 img 没有 wp-image 类」 这类结构取不到原图。已接进三级兜底,并固化成反向注入 S12 / S13。 ### 8. 发布与实测 - 代码提交:`7125fdd` + `1cf4267`(含上面的兜底修复),均已推送 - 已发到更新通道 `/theme-update/`:`update.json` = 3.2.40,zip 与 readme 均 200, 下载 md5 与磁盘一致 - **服务器上的主题目录仍是 3.2.39,没有被覆盖** —— 请在 WP 后台点「升级」 升级后请重点试这几项: 1. 单图文章:点一下 → 当前页盖住、图清楚、底下隐约可见;Esc / 点空白 / 点 × 都能退 2. 多图文章:左右箭头、键盘 ←→↑↓ 都能切,底部「第几张 / 共几张」跟着变 3. 后台把「遮罩不透明度」调到 70% / 60%,看还读不读得清(尤其深色图片多的页面) 4. 后台切到「深色遮罩」,再调一次不透明度 —— 这是当初那个优先级 bug 的复现场景 5. 关掉「启用图片灯箱」→ 点图应退回「新标签页打开原图」 6. 手机上点开图片,看箭头是否够大好点、底部文字会不会被遮 哪一项不对请直接说现象,我这边按工单继续跟。等您确认没问题再关单。
Author
Owner

性能优化已交付并实测确认(3.2.41)

用户反馈:「点一张图之后先把白色背景渲染出来,然后这个背景再出来,速度特别慢」。
已发布 3.2.41,用户实测确认「确实比以前快了不少」。

1. 归因(先量化,不猜)

自建慢速 HTTP 服务(32KB 分块 + 按带宽 sleep)实测 3.2 Mbps:

    31ms  遮罩=True   图片有像素=False
  6005ms  遮罩=True   图片有像素=False

⇒ 遮罩从来不是瓶颈(31ms 一帧画完)。慢在原图下载:线上正文原图最大 3.2 MB
(PUA的两面性与界定-scaled.png),3 Mbps 下需 8.7 秒。这期间屏幕上是「整片白 +
一个 0×0 空 img」,即用户看到的「白背景先出来、图后到」。

2. 三层改造

改动 做法
缩略图占位(blur-up) 点开瞬间把正文里 img.currentSrc(已解码的那份)顶上,0 网络请求
原图淡入 is-ready 后 opacity: 0→1,避免看到「上半张清晰下半张空白」
空闲预取 requestIdleCallback 串行预取视口上下 1.5 屏内原图,new Image() 不塞 DOM

预取三条约束:只取视口附近(文章再长不拉全篇)· 串行(一次一个,不抢正文带宽)·
尊重 saveData / slow-2g(那种网络下预取纯浪费)。

CSS 的 .yang-lb-stage 从 flex 改 grid + place-items:center,三层都 grid-area: 1/1
同格叠放(flex 会让子元素并排;absolute 又不参与居中,得手算偏移)。

3. 实测对比

场景 遮罩 首帧有像素 清晰原图
改造前 31ms 6 秒后仍无 —
改造后·立刻点(最坏) 25ms 25ms 15.0s
改造后·读 12 秒后点(常见) 25ms 25ms 25ms(预取命中)

服务端日志:两张图各只请求 1 次 ⇒ 预取一次命中,无重复下载。

4. 验证

  • 浏览器真实交互 245/0(新增 45 项专测占位层与加载态)
  • 服务端正文改写 55/0(PHP 8.4 + 8.5)
  • 演示页 34/0
  • 反向验证 27/27:把代码故意改坏 27 处,全部被抓到且失败项正是预期目标
    (新增 4 处专打本版:占位层吃指针 / 原图永远 opacity:0 / 已就绪还在转圈 / 原图层没 z-index)
  • 包内契约 144/0 + 该段注入 15/15
  • 发布脚本新增 2.1l/2.1m/2.1n 段,含两条反向注入

修掉的两个隐藏问题值得一提:① 「预取串行」判据初版只查两个赋值是否存在,
删掉 if (prefetching || ...) 守卫后仍然绿——而后果是几十张原图并发抢带宽;
② classList.remove('is-loading') 在 onImgReady 和 onerror 各有一处,
整文件 grep 恒假绿,后果是「图清晰了转圈还在转,看着像卡死」。

5. 发布

Git f3583e0(带 Refs #10)· zip md5 0c033ac0df40b267f09ae59d55d2f68c ·
线上 update.json = 3.2.41 · 服务器主题目录未覆盖,由用户在后台升级。
改动仅 3 个文件:assets/css/lightbox.css、assets/js/lightbox.js、style.css,
无新增文件、未动后台选项、无需重新配置。

用户已实测确认效果,本工单关闭。

## 性能优化已交付并实测确认(3.2.41) 用户反馈:「点一张图之后先把白色背景渲染出来,然后这个背景再出来,速度特别慢」。 已发布 3.2.41,用户实测确认「确实比以前快了不少」。 ### 1. 归因(先量化,不猜) 自建慢速 HTTP 服务(32KB 分块 + 按带宽 sleep)实测 3.2 Mbps: ``` 31ms 遮罩=True 图片有像素=False 6005ms 遮罩=True 图片有像素=False ``` ⇒ **遮罩从来不是瓶颈**(31ms 一帧画完)。慢在**原图下载**:线上正文原图最大 3.2 MB (`PUA的两面性与界定-scaled.png`),3 Mbps 下需 8.7 秒。这期间屏幕上是「整片白 + 一个 0×0 空 img」,即用户看到的「白背景先出来、图后到」。 ### 2. 三层改造 | 改动 | 做法 | |---|---| | 缩略图占位(blur-up) | 点开瞬间把正文里 `img.currentSrc`(**已解码**的那份)顶上,0 网络请求 | | 原图淡入 | `is-ready` 后 `opacity: 0→1`,避免看到「上半张清晰下半张空白」 | | 空闲预取 | `requestIdleCallback` 串行预取视口上下 1.5 屏内原图,`new Image()` 不塞 DOM | 预取三条约束:只取视口附近(文章再长不拉全篇)· **串行**(一次一个,不抢正文带宽)· 尊重 `saveData` / `slow-2g`(那种网络下预取纯浪费)。 CSS 的 `.yang-lb-stage` 从 `flex` 改 `grid + place-items:center`,三层都 `grid-area: 1/1` 同格叠放(flex 会让子元素并排;absolute 又不参与居中,得手算偏移)。 ### 3. 实测对比 | 场景 | 遮罩 | 首帧有像素 | 清晰原图 | |---|---|---|---| | 改造前 | 31ms | **6 秒后仍无** | — | | 改造后·立刻点(最坏) | 25ms | **25ms** | 15.0s | | 改造后·读 12 秒后点(常见) | 25ms | 25ms | **25ms(预取命中)** | 服务端日志:两张图**各只请求 1 次** ⇒ 预取一次命中,无重复下载。 ### 4. 验证 - 浏览器真实交互 **245/0**(新增 45 项专测占位层与加载态) - 服务端正文改写 **55/0**(PHP 8.4 + 8.5) - 演示页 **34/0** - **反向验证 27/27**:把代码故意改坏 27 处,全部被抓到且失败项正是预期目标 (新增 4 处专打本版:占位层吃指针 / 原图永远 opacity:0 / 已就绪还在转圈 / 原图层没 z-index) - 包内契约 **144/0** + 该段注入 **15/15** - 发布脚本新增 2.1l/2.1m/2.1n 段,含两条反向注入 修掉的两个隐藏问题值得一提:① 「预取串行」判据初版只查两个赋值是否存在, 删掉 `if (prefetching || ...)` 守卫后仍然绿——而后果是几十张原图并发抢带宽; ② `classList.remove('is-loading')` 在 `onImgReady` 和 `onerror` 各有一处, 整文件 grep 恒假绿,后果是「图清晰了转圈还在转,看着像卡死」。 ### 5. 发布 Git `f3583e0`(带 `Refs #10`)· zip md5 `0c033ac0df40b267f09ae59d55d2f68c` · 线上 update.json = 3.2.41 · **服务器主题目录未覆盖**,由用户在后台升级。 改动仅 3 个文件:`assets/css/lightbox.css`、`assets/js/lightbox.js`、`style.css`, 无新增文件、未动后台选项、无需重新配置。 用户已实测确认效果,本工单关闭。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: laoyang/wordpress-kratos-yang#10