文章讲 WordPress 大图插入与灯箱改造:截图、表格等高信息图应正文插完整尺寸,原图宽控在1920内并转WebP,长图不要裁切;灯箱需支持滚轮无级缩放和按住拖动,而不是只切换适应屏幕与原始尺寸。改媒体尺寸只对新图生效,旧图需重建缩略图,相关代码已实测可用。
前几天发 MosDNS 实时统计面板 那篇,正文里贴了好几张 1920 宽的后台截图。发完自己点进去一看,图被压成 409px 宽的一条,鼠标移上去只有一个放大镜图标;点开灯箱之后更难受——只能在「适应屏幕」和「原始尺寸」之间二选一,切到原始尺寸图片比屏幕还大,想看左上角,滚不动。
这事儿不能怪 WordPress,是我自己没把两件事串起来想:一是图该怎么插进去,二是插进去之后读者怎么才能看清。这篇两个都讲,后半段的代码是我昨天刚给自己站改完、实测过的,可以直接拿去用。
一 · 先说结论,不想看原理的直接照做
三条,按顺序做:
- 截图、表格这类信息密度高的图,正文里就插「完整尺寸」,别插 medium,字会糊成一团。前提是原图宽度控制在 1920 以内、转 WebP。
- 长图绝对不要裁切。Square 缩略图用来做列表封面没问题,正文里一定要保原始比例。
- 灯箱必须支持滚轮缩放 + 按住拖动。只给一个「放大/缩小」按钮是不够用的,放大的图一定有超出屏幕的部分。
第三条是本篇的重点,代码在第三节,坑在第四节。
二 · WordPress 插大图:先搞清楚它到底给你生成了几个尺寸
你往媒体库传一张 1920×3400 的截图,WordPress 不会只存一份。它会按后台「设置 → 媒体」里的配置额外切出好几个尺寸,full 之外的都是自动生成的。

| 尺寸名 | 默认生成规则 | 一般用在哪 | 我的建议 |
|---|---|---|---|
thumbnail |
150×150,硬裁成正方形 | 后台列表、小头像 | 改成 300×200 硬裁,跟列表卡片比例对上 |
medium |
最长边 300,等比 | 老版本编辑器默认 | 放宽到 640,否则正文字太小 |
medium_large |
宽 768,等比 | 响应式 srcset 中间档 | 保留,移动端用得上 |
large |
最长边 1024,等比 | 正文大图 | 放宽到 1280 |
full |
原图,不动 | 灯箱、需要看细节的图 | 宽度上限压到 1920,转 WebP |
改设置只对之后上传的图生效
这点很容易踩。你在后台把 medium 从 300 改成 640,已经传过的图不会重新生成。有 WP-CLI 的话跑 wp media regenerate;没有的话装个 Regenerate Thumbnails 插件,或者在主题里临时挂一段代码批量重建。别像我第一次那样,改完发现前台没变化,还以为缓存没清。
1. 在主题里写死尺寸,比在后台点更靠谱
后台设置会被插件、主题、甚至一次误点改掉。我更喜欢在 functions.php 里写死:
add_action('init', function () {
update_option('thumbnail_size_w', 300);
update_option('thumbnail_size_h', 200);
update_option('thumbnail_crop', 1); // 1 = 硬裁到 300x200
update_option('medium_size_w', 640);
update_option('medium_size_h', 0); // 0 = 高度自适应,不裁
update_option('medium_large_size_w', 768);
update_option('medium_large_size_h', 0);
update_option('large_size_w', 1280);
update_option('large_size_h', 0);
});
// 用不上的档位直接摘掉,省磁盘也省生成时间
add_filter('intermediate_image_sizes_advanced', function ($sizes) {
unset($sizes['medium_large']);
return $sizes;
});
主题自己还会注册额外尺寸(我这个主题给首页列表注册了一档 -300x200)。想知道你的站到底生成了哪些,临时打印一下:
add_action('wp_footer', function () {
if (!current_user_can('manage_options')) return;
echo '<pre>';
foreach (get_intermediate_image_sizes() as $s) {
echo $s . ' ';
}
echo '</pre>';
});
2. 正文里插哪一档:按图的用途分
我自己的分法很简单,看这张图是「氛围」还是「信息」。
| 图的类型 | 插哪一档 | 理由 |
|---|---|---|
| 封面、氛围图、产品照 | large(1280) |
看个大概就够,省流量 |
| 后台截图、代码截图、表格 | full |
有文字,缩小一档就看不清了 |
| 手机竖截图 | full,宽度 720 左右 |
本身就不大,别再压 |
| 超长流程图(高度是宽度 3 倍以上) | full,并且在文里额外给个直接打开原图的链接 |
再好用的灯箱,看 4000px 的长图也累 |
插原图会不会拖慢首屏?会一点,但没你想的那么严重:WordPress 从 5.5 起默认给正文图片加 loading="lazy",屏幕外的图不会在首屏下载。真正要管的是原图体积——我给自己定的规矩是宽度不超过 1920、转 WebP(质量 82)、单图控制在 300KB 以内。超过就先压再传。
3. 别让缩略图把长图裁了
这是我最开始犯的错。有一张 1920×3400 的竖向截图,被主题的列表缩略图硬裁成 300×200 之后,首页卡片上只剩中间一条,完全看不出是什么。所以给文章设封面图的时候,一定要确认那一档缩略图是「等比」还是「硬裁」。硬裁的档位只适合横向图。
三 · 灯箱改造:滚轮缩放 + 按住左键拖动
图插得再大,读者看不清也是白搭。主题自带的灯箱一般只做一件事:点一下,在「适应屏幕」和「原始尺寸」之间切换。这个交互有两个硬伤:
- 没有中间档。要么整张图缩到屏幕里(细节全没),要么直接 1:1(超出屏幕一大截)。
- 放大之后没法看全。图比容器大,超出的部分滚不到,尤其左上角——这是 CSS flex 居中的一个老问题,下面细说。
我想要的是看图软件那种手感:滚轮无级缩放,按住左键上下左右拖。下面这段是精简后的核心逻辑,依赖主题已有的灯箱 DOM(我这个主题的遮罩层结构是 .lightbox-overlay > .lightbox-body > img.lightbox-image,你按自己的改选择器)。
1. 缩放:以光标为锚点
关键在「锚点」——滚轮缩放时光标指着的那一点应该保持不动,否则放大几下之后你关心的位置早就跑出屏幕了。做法是缩放前后各量一次图片位置,反推滚动补偿:
function zoomAt(cx, cy, factor) {
var before = img.getBoundingClientRect();
if (!before.width) return;
// 光标落在图片的哪个比例位置
var ux = (cx - before.left) / before.width;
var uy = (cy - before.top) / before.height;
z = Math.min(8, Math.max(1, z * factor));
applyScale(); // 按 z 重设图片宽度
var after = img.getBoundingClientRect();
// 让同一个比例点回到光标下面
body.scrollLeft += (after.left - (cx - ux * after.width));
body.scrollTop += (after.top - (cy - uy * after.height));
}
overlay.addEventListener('wheel', function (e) {
e.preventDefault(); // 别让页面跟着滚
var d = e.deltaY * (e.deltaMode === 1 ? 16 : 1);
zoomAt(e.clientX, e.clientY, Math.exp(-d * 0.0018));
}, { passive: false });
Math.exp 那一下是为了让缩放手感是「等比」的:每一格滚轮的变化量跟当前倍数成正比,而不是固定加减多少像素。deltaMode 那个三元是兼容 Firefox——它有些版本给的是「行数」不是像素。
2. 拖动:按住左键平移
function startDrag(cx, cy) {
if (z <= 1.005) return; // 没放大就不拖
dragging = true; moved = 0;
sx = cx; sy = cy;
ox = body.scrollLeft; oy = body.scrollTop;
img.classList.add('n997-dragging');
}
function moveDrag(cx, cy) {
if (!dragging) return;
body.scrollLeft = ox - (cx - sx);
body.scrollTop = oy - (cy - sy);
}
img.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
e.preventDefault(); // 关键:挡掉浏览器「把图片拖出去」的原生行为
startDrag(e.clientX, e.clientY);
});
document.addEventListener('mousemove', function (e) {
moveDrag(e.clientX, e.clientY);
});
document.addEventListener('mouseup', endDrag);
e.preventDefault() 那行别省。不加的话按住一拖,浏览器会把图片当文件拖出来,灯箱原地不动。
另外记得:拖动之后会触发 click。如果你还保留了「单击图片切换」的逻辑,得在 mouseup 时判断位移,超过 6px 就不当点击处理,否则拖完一松手图片就跳回适应尺寸了。
四 · 两个 flex 坑,不改的话放大功能等于白做
这两个是我在无头浏览器里量出来的,不是想当然。代码写对了,但显示效果不对,问题都在这。
坑一:flex 居中会让溢出的两边都滚不到
灯箱的容器一般是这么写的(居中 + 允许滚动):
.lightbox-body {
display: flex;
align-items: center;
justify-content: center;
width: 100%; height: 100%;
overflow: auto;
}
看起来没毛病。但当图片比容器大的时候,justify-content: center 会把超出的部分平均分到左右两侧。而 scrollLeft 不能为负,左边那半截就永远滚不到。我实测放大到 800% 时,scrollLeft = 0 状态下图片左边缘在 -240px,也就是说最左边 240 像素的内容这辈子都看不到。

修法不是去掉居中,而是把居中这件事交给图片的 margin:
.lightbox-overlay .lightbox-body {
overflow: auto !important;
justify-content: flex-start !important;
align-items: flex-start !important;
}
.lightbox-overlay .lightbox-image {
margin: auto !important;
flex-shrink: 0 !important;
}
margin: auto 的行为很理想:容器有富余空间时,外边距吃掉剩余空间,图片照样居中;一旦内容溢出,auto 外边距自动归零,图片老老实实从起始边开始排,四边都能滚到。
坑二:图片作为 flex item 会被压回原始尺寸
改完上面那条我以为好了,实测发现放大到某个倍数就不动了——800% 时宽度卡在 1920。巧了,1920 正好是图片原始宽度。
原因是 flex item 默认 flex-shrink: 1,容器装不下时会被压缩,一直压到「自动最小尺寸」。对图片来说这个最小值就是它的原始宽度。所以放大倍数再高,渲染出来最多也只有 1:1。
一行解决:flex-shrink: 0。这就是上面那段 CSS 里第二行的来历。
这两个坑跟主题无关
凡是「flex 居中 + 子元素可能溢出 + 需要滚动」的组合都会中招,不只是灯箱。表格容器、横向卡片列表、代码框都有同样的隐患。遇到「左边/上边看不到」的情况,先怀疑 justify-content: center。
五 · 改完怎么确认真的生效
截图看一眼不算验证,得让浏览器真的去点、去滚、去拖,然后把数值读出来。我用的是 Chrome 的 CDP 协议,Node 22 自带 fetch 和 WebSocket,不用装任何依赖。
// 连上 headless Chrome 后,直接派发事件
await send('Page.navigate', { url: articleUrl });
await sleep(6000);
// 1. 打开灯箱
await ev(`document.querySelectorAll('.single-article__content img')[0].click()`);
await sleep(2500);
// 2. 滚轮放大 4 格
await ev(`
var ov = document.querySelector('.lightbox-overlay');
var r = img.getBoundingClientRect();
for (var i = 0; i < 4; i++)
ov.dispatchEvent(new WheelEvent('wheel', {
deltaY: -100, clientX: r.left + r.width/2,
clientY: r.top + r.height/2, bubbles: true, cancelable: true
}));
img.offsetWidth;
`);
// 3. 按住左键往下拖,看能不能滚到顶部
await ev(`
img.dispatchEvent(new MouseEvent('mousedown', {clientX: cx, clientY: cy, button: 0, bubbles: true}));
for (var i = 1; i <= 30; i++)
document.dispatchEvent(new MouseEvent('mousemove', {clientX: cx, clientY: cy + i*30, bubbles: true}));
document.dispatchEvent(new MouseEvent('mouseup', {bubbles: true}));
body.scrollTop; // 应该是 0
`);
改完之后我自己站上的实测结果:
| 检查项 | 改之前 | 改之后 |
|---|---|---|
| 适应屏幕宽度 | 409px | 409px(居中不变) |
| 滚轮放大 4 格 | 只能跳到 1:1 | 798px,显示 205% |
| 放大后能否滚到顶部 | 不行 | 可以,scrollTop 归 0 |
| 800% 时图片宽度 | 1920px(卡在原始尺寸) | 3108px |
| 四边可达性 | 左边 -240px,滚不到 | 四边全部可达 |

六 · 小结
回头看,这件事折腾的其实不是 WordPress,是两个一直没被认真对待的细节:图片插进来之后读者要怎么看,以及 CSS 里那两行居中到底在溢出时干了什么。
如果你只想抄最小可用的一套:正文插原图、原图宽度压到 1920 以内转 WebP、灯箱加滚轮缩放和拖动、容器改 flex-start + 图片加 margin:auto 和 flex-shrink:0。做完这四步,读者在你的截图上是能真的看清楚字。
顺带一提,用 nginx 日志自建访问统计 那篇里也有类似的思路——先把数据拿到手,再花力气把它显示清楚。工具是死的,让人看懂才是目的。
- 文章名称:WordPress 插入大图看不清怎么办:多档尺寸怎么选 + 灯箱滚轮缩放改造
- 文章永久网址:https://www.997wan.com/wordpress-large-image-and-lightbox-zoom/
- 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长 997笔记 进行删除处理。
- 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
- 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
- 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
- 如无特别声明本文即为原创文章仅代表个人观点,版权归《997笔记》所有,欢迎转载,转载请保留原文链接。