前言:为什么多图字段需要独立短代码?

在之前的文章中,我们解决了 ACF 纯文本字段在 WooCommerce 产品列表页的自动展示问题。但在实际业务中,产品详情页往往需要展示多张高清细节图(例如包装图、细节拆解图)。

如果你在 ACF 中创建字段组(如 Product detail image),并使用了类似 Marbak Multi Image 这样的多图上传插件,你会发现它的数据结构与普通文本完全不同。

特别是当你在后台将 Return Format(返回格式)设置为 Image Array(图片数组) 时,get_field() 返回的是一个包含多个维度的复杂数组(包含图片 ID、URL、尺寸等)。如果我们沿用之前的纯文本短代码,前端只会输出一堆 Array 报错。

因此,我们需要单独编写一个专门处理“图片数组”的独立短代码,不仅要精准提取图片链接,还要兼顾前端排版和页面性能。

需求分析与设计思路

我们的目标是:

  1. 独立运行:与之前的列表页参数、纯文本短代码互不干扰,代码彻底解耦。
  2. 精准提取:针对 Image Array 格式,提取特定尺寸(如 large/full)的图片链接,而非直接加载原图,节省带宽。
  3. 性能优化:输出 <img> 标签时带上懒加载(lazy loading)属性。
  4. 样式友好:输出的 HTML 结构需带有特定的 Class,方便通过 CSS 做成美观的网格相册。

⚠️ 核心避坑:Name 与 Label 的区别

在编写和使用短代码之前,必须明确 ACF 字段的命名规则,这是新手最容易出错的地方:

  • Field Label(字段标签):显示在 WordPress 后台给编辑人员看的,可以是中文或带空格的英文(例如:product single page detail image)。它不能用于代码调用。
  • Field Name(字段名称):用于 PHP 代码、短代码中调用的唯一标识符。必须是英文、数字或下划线,且不能有空格(例如:lier_product_single_page_detail_image)。

👉 所以,我们在短代码里填写的 name,必须是自定义字段的 Name(字段名称),而不是 Label!

步骤一:在 functions.php 中注册独立的多图短代码

请将以下代码添加到你当前子主题的 functions.php 文件中。为了保持前缀一致性,我们将短代码命名为 [lier_acf_image_gallery]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php
/**
* ========================================================================
* Lier ACF 独立短代码 - 输出多图字段 (支持 Marbak Multi Image 等返回 Image Array 的字段)
* ========================================================================
*
* 【用法示例】:
*
* 1. 最基础用法(直接输出你截图里的多图字段):
* [lier_acf_image_gallery name="lier_product_single_page_detail_image"]
*
* 2. 自定义图片大小(可选 thumbnail, medium, large, full,默认 full):
* [lier_acf_image_gallery name="lier_product_single_page_detail_image" size="large"]
*
* 3. 自定义外层容器样式(方便前端 CSS 布局):
* [lier_acf_image_gallery name="lier_product_single_page_detail_image" class="my-custom-gallery-class"]
*
* ========================================================================
*/

if ( ! function_exists( 'lier_acf_image_gallery_shortcode' ) ) {
function lier_acf_image_gallery_shortcode( $atts ) {
// 1. 定义默认参数
$atts = shortcode_atts(
array(
'name' => '', // ACF 字段的 Name (例如 lier_product_single_page_detail_image)
'size' => 'full', // 图片尺寸 (thumbnail, medium, large, full)
'class' => '', // 自定义容器 class
'post_id' => false, // 默认获取当前产品ID
),
$atts,
'lier_acf_image_gallery'
);

// 如果没有填 name,直接返回
if ( empty( $atts['name'] ) ) {
return '';
}

// 2. 获取当前文章/产品 ID
$post_id = $atts['post_id'] ? $atts['post_id'] : get_the_ID();

// 3. 获取 ACF 字段值(因为你在后台设置的是 Image Array,这里返回的会是数组)
$images = get_field( $atts['name'], $post_id );

// 4. 如果没图片,直接返回空
if ( empty( $images ) || ! is_array( $images ) ) {
return '';
}

// 5. 组装输出 HTML
// 加上自定义的 class,方便你在 Avada 里排版
$custom_class = ! empty( $atts['class'] ) ? ' ' . esc_attr( $atts['class'] ) : '';
$output = '<div class="lier-acf-gallery-wrapper' . $custom_class . '">';

// 6. 循环遍历图片数组
foreach ( $images as $image ) {
// 由于你设置的是 Image Array,$image 里面包含了各种尺寸的 URL 和 ID
// 我们根据短代码传入的 size 参数来提取对应的 URL
if ( isset( $image['sizes'][ $atts['size'] ] ) ) {
$img_url = $image['sizes'][ $atts['size'] ];
} else {
// 如果没有指定的尺寸,则降级使用原图 URL
$img_url = $image['url'];
}

$img_alt = ! empty( $image['alt'] ) ? esc_attr( $image['alt'] ) : '';
$img_title = ! empty( $image['title'] ) ? esc_attr( $image['title'] ) : '';

// 输出单张图片的结构(你可以通过修改这里的 HTML 结构来配合 Avada 的网格系统)
$output .= '<div class="lier-gallery-item">';
$output .= '<img src="' . esc_url( $img_url ) . '" alt="' . $img_alt . '" title="' . $img_title . '" loading="lazy" />';
$output .= '</div>';
}

$output .= '</div>';

return $output;
}
}
// 注册这个新的短代码
add_shortcode( 'lier_acf_image_gallery', 'lier_acf_image_gallery_shortcode' );

步骤二:在 Avada 详情页中调用

代码注册成功后,你就可以在需要展示图集的产品详情页中进行调用。打开 Avada Builder,拖入一个 Code Block 或 Text Block(记得切换到文本/HTML 模式),粘贴以下短代码:

1
[lier_acf_image_gallery name="lier_product_single_page_detail_image"]

(注意:请务必对照 ACF 后台的 Name 列来填写,不要填成带有空格的 Label)

进阶调用方式:
如果你希望图片稍微小一点,并加一个自定义的 Class 来单独控制这个页面的布局,可以这样写:

1
[lier_acf_image_gallery name="lier_product_single_page_detail_image" size="large" class="product-detail-grid"]

步骤三:添加 CSS 样式实现网格相册

默认情况下,多图输出是垂直堆叠的,这会把页面拉得非常长。我们建议在 Avada > Options > Custom CSS 中加入以下 CSS,将其变成一个 自动换行的响应式网格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* 多图相册外层容器 */
.lier-acf-gallery-wrapper {
display: flex;
flex-wrap: wrap; /* 允许自动换行 */
gap: 15px; /* 图片之间的间距 */
margin-top: 20px;
margin-bottom: 20px;
}

/* 单个图片容器 */
.lier-acf-gallery-wrapper .lier-gallery-item {
/* 默认设置每行放 3 张图,按 33.33% 减去间距计算 */
flex: 0 0 calc(33.333% - 10px);
max-width: calc(33.333% - 10px);
border-radius: 8px; /* 稍微圆角 */
overflow: hidden; /* 防止图片溢出 */
border: 1px solid #eee; /* 加个浅边框,可选 */
}

/* 图片自适应 */
.lier-acf-gallery-wrapper .lier-gallery-item img {
width: 100%;
height: auto;
display: block;
object-fit: cover;
transition: transform 0.3s ease; /* 添加悬浮动画准备 */
}

/* 可选:鼠标悬浮时图片轻微放大 */
.lier-acf-gallery-wrapper .lier-gallery-item:hover img {
transform: scale(1.05);
}

/* 手机端适配:屏幕较小时,每行放 2 张 */
@media (max-width: 768px) {
.lier-acf-gallery-wrapper .lier-gallery-item {
flex: 0 0 calc(50% - 8px);
max-width: calc(50% - 8px);
}
}

为什么这段代码更科学?

  1. 完美匹配 Image Array:因为你在后台设置了返回格式是数组,代码中通过 $image['sizes'][ $atts['size'] ] 能够精准提取 WordPress 自动生成的缩略图尺寸,而不是强行加载几兆的高清原图,极大地提升了页面加载速度。
  2. 懒加载机制:<img loading="lazy"> 是 HTML5 的原生属性,它告诉浏览器只有当用户滑动到图片附近时,才真正去下载这张图片。
  3. 强隔离性:短代码专属的 lier-acf-gallery-wrapper 容器类名,确保你调整图片网格的 CSS 不会意外影响到页面的其他部分。

如果想要输出的图片美观和可以点击弹窗展示可以把以上代码换成如下方案:

下面把前面所有改动整合成一份完整可替换的最终方案。你直接用这段覆盖原来的短代码文件即可,后台只需传图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
<?php
/**
* ========================================================================
* Lier ACF 独立短代码 - 输出多图字段 (高端响应式画廊 + 灯箱)
* ========================================================================
*
* 【用法示例】:
* 1. 基础用法:
* [lier_acf_image_gallery name="lier_product_single_page_detail_image"]
*
* 2. 自定义图片大小(thumbnail, medium, large, full,默认 large):
* [lier_acf_image_gallery name="lier_product_single_page_detail_image" size="large"]
*
* 3. 自定义外层容器样式:
* [lier_acf_image_gallery name="lier_product_single_page_detail_image" class="my-custom-gallery-class"]
*
* ========================================================================
*/
if ( ! function_exists( 'lier_acf_image_gallery_shortcode' ) ) {
function lier_acf_image_gallery_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'name' => '',
'size' => 'large',
'class' => '',
'post_id' => false,
'caption' => 'yes',
),
$atts,
'lier_acf_image_gallery'
);

if ( empty( $atts['name'] ) ) {
return '';
}

$post_id = $atts['post_id'] ? $atts['post_id'] : get_the_ID();
$images = get_field( $atts['name'], $post_id );

if ( empty( $images ) || ! is_array( $images ) ) {
return '';
}

$gallery_id = 'lier-gallery-' . wp_rand( 1000, 999999 );
$custom_class = ! empty( $atts['class'] ) ? ' ' . esc_attr( $atts['class'] ) : '';

$show_caption = ( strtolower( $atts['caption'] ) !== 'no' && strtolower( $atts['caption'] ) !== 'false' && $atts['caption'] !== '0' );

$items = array();
foreach ( $images as $image ) {
$img_url = isset( $image['sizes'][ $atts['size'] ] ) ? $image['sizes'][ $atts['size'] ] : $image['url'];
$full_url = ! empty( $image['sizes']['full'] ) ? $image['sizes']['full'] : $image['url'];
$img_alt = ! empty( $image['alt'] ) ? esc_attr( $image['alt'] ) : '';
$img_title = ! empty( $image['title'] ) ? esc_attr( $image['title'] ) : '';
$items[] = array(
'thumb' => $img_url,
'full' => $full_url,
'alt' => $img_alt,
'caption' => $img_title,
);
}

$output = '<div class="lier-acf-gallery-wrapper' . $custom_class . '" id="' . esc_attr( $gallery_id ) . '" data-lier-gallery="1">';
$output .= '<div class="lier-gallery-grid">';

foreach ( $items as $index => $item ) {
$output .= '<figure class="lier-gallery-item" data-lier-index="' . esc_attr( $index ) . '">';
$output .= '<img src="' . esc_url( $item['thumb'] ) . '" alt="' . $item['alt'] . '" title="' . $item['caption'] . '" loading="lazy" />';
if ( ! empty( $item['caption'] ) ) {
$output .= '<figcaption class="lier-gallery-caption">' . esc_html( $item['caption'] ) . '</figcaption>';
}
$output .= '</figure>';
}

$output .= '</div>';
$output .= '<script type="application/json" class="lier-gallery-data">' . wp_json_encode( $items ) . '</script>';
$output .= '</div>';

return $output;
}
}
add_shortcode( 'lier_acf_image_gallery', 'lier_acf_image_gallery_shortcode' );

if ( ! function_exists( 'lier_acf_image_gallery_assets' ) ) {
function lier_acf_image_gallery_assets() {
?>
<style>
.lier-acf-gallery-wrapper { width: 100%; margin: 24px 0; }
.lier-gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.lier-gallery-item {
position: relative;
margin: 0;
overflow: hidden;
border-radius: 10px;
background: #f5f5f5;
aspect-ratio: 4 / 3;
cursor: zoom-in;
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
transition: transform .25s ease, box-shadow .25s ease;
}
.lier-gallery-item:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.lier-gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform .4s ease;
}
.lier-gallery-item:hover img { transform: scale(1.04); }
.lier-gallery-caption {
position: absolute;
left: 10px;
bottom: 10px;
max-width: calc(100% - 20px);
padding: 3px 8px;
font-size: 11px;
line-height: 1.3;
color: #fff;
background: rgba(0,0,0,0.55);
border-radius: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
-webkit-backdrop-filter: blur(2px);
backdrop-filter: blur(2px);
}

.lier-lightbox {
position: fixed;
inset: 0;
z-index: 999999;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
background: rgba(0,0,0,0.88);
opacity: 0;
transition: opacity .25s ease;
}
.lier-lightbox.is-open { display: flex; opacity: 1; }
.lier-lightbox-inner {
position: relative;
max-width: min(1200px, 100%);
max-height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.lier-lightbox img {
max-width: 100%;
max-height: calc(100vh - 120px);
width: auto;
height: auto;
border-radius: 8px;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
object-fit: contain;
user-select: none;
-webkit-user-drag: none;
}
.lier-lightbox-caption {
margin-top: 12px;
font-size: 13px;
color: rgba(255,255,255,0.85);
text-align: center;
}
.lier-lightbox-close {
position: absolute;
top: -44px;
right: 0;
width: 36px;
height: 36px;
border: none;
border-radius: 50%;
background: rgba(255,255,255,0.15);
color: #fff;
font-size: 22px;
line-height: 36px;
text-align: center;
cursor: pointer;
transition: background .2s;
}
.lier-lightbox-close:hover { background: rgba(255,255,255,0.3); }

.lier-lightbox-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 44px;
height: 44px;
border: none;
border-radius: 50%;
background: rgba(255,255,255,0.15);
color: #fff;
font-size: 24px;
line-height: 44px;
text-align: center;
cursor: pointer;
transition: background .2s;
z-index: 2;
padding: 0;
}
.lier-lightbox-nav:hover { background: rgba(255,255,255,0.3); }
.lier-lightbox-prev { left: -60px; }
.lier-lightbox-next { right: -60px; }

@media (max-width: 1024px) {
.lier-gallery-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.lier-lightbox-prev { left: -8px; }
.lier-lightbox-next { right: -8px; }
}
@media (max-width: 768px) {
.lier-gallery-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
.lier-gallery-item { aspect-ratio: 1 / 1; border-radius: 8px; }
.lier-gallery-caption { font-size: 10px; left: 6px; bottom: 6px; padding: 2px 6px; }
.lier-lightbox { padding: 16px 20px; }
.lier-lightbox img { max-height: calc(100vh - 100px); border-radius: 6px; }
.lier-lightbox-close { top: -40px; width: 32px; height: 32px; line-height: 32px; font-size: 20px; }
.lier-lightbox-nav {
width: 36px;
height: 36px;
line-height: 36px;
font-size: 20px;
top: auto;
bottom: -48px;
transform: none;
}
.lier-lightbox-prev { left: calc(50% - 48px); }
.lier-lightbox-next { right: calc(50% - 48px); }
}
@media (max-width: 480px) {
.lier-lightbox { padding: 12px 16px; }
}
</style>

<script>
(function () {
if (window.lierGalleryInit) return;
window.lierGalleryInit = true;

var state = { items: [], index: 0, box: null };

function ensureLightbox() {
if (state.box) return state.box;
var box = document.createElement('div');
box.className = 'lier-lightbox';
box.innerHTML =
'<div class="lier-lightbox-inner">' +
'<button type="button" class="lier-lightbox-close" aria-label="Close">&times;</button>' +
'<button type="button" class="lier-lightbox-nav lier-lightbox-prev" aria-label="Previous">&#10094;</button>' +
'<img src="" alt="" />' +
'<button type="button" class="lier-lightbox-nav lier-lightbox-next" aria-label="Next">&#10095;</button>' +
'<div class="lier-lightbox-caption"></div>' +
'</div>';
document.body.appendChild(box);
state.box = box;
return box;
}

function render() {
var box = ensureLightbox();
var item = state.items[state.index];
if (!item) return;
var img = box.querySelector('img');
var cap = box.querySelector('.lier-lightbox-caption');
img.src = item.full;
img.alt = item.caption || '';
cap.textContent = item.caption || '';
cap.style.display = item.caption ? '' : 'none';
var prev = box.querySelector('.lier-lightbox-prev');
var next = box.querySelector('.lier-lightbox-next');
var multi = state.items.length > 1;
prev.style.display = multi ? '' : 'none';
next.style.display = multi ? '' : 'none';
}

function openLightbox(items, index) {
state.items = items;
state.index = index;
var box = ensureLightbox();
render();
box.classList.add('is-open');
document.body.style.overflow = 'hidden';
}

function closeLightbox() {
if (!state.box) return;
state.box.classList.remove('is-open');
document.body.style.overflow = '';
}

function go(delta) {
if (state.items.length < 2) return;
state.index = (state.index + delta + state.items.length) % state.items.length;
render();
}

function getGalleryData(wrapper) {
var dataEl = wrapper.querySelector('.lier-gallery-data');
if (!dataEl) return null;
try { return JSON.parse(dataEl.textContent); } catch (e) { return null; }
}

document.addEventListener('click', function (e) {
var item = e.target.closest('.lier-gallery-item');
if (item) {
var wrapper = item.closest('.lier-acf-gallery-wrapper');
if (!wrapper) return;
var items = getGalleryData(wrapper);
if (!items || !items.length) return;
var idx = parseInt(item.getAttribute('data-lier-index'), 10) || 0;
openLightbox(items, idx);
return;
}

if (!state.box || !state.box.classList.contains('is-open')) return;

if (e.target.closest('.lier-lightbox-close') ||
e.target === state.box ||
e.target.classList.contains('lier-lightbox-inner')) {
closeLightbox();
} else if (e.target.closest('.lier-lightbox-prev')) {
go(-1);
} else if (e.target.closest('.lier-lightbox-next')) {
go(1);
}
});

document.addEventListener('keydown', function (e) {
if (!state.box || !state.box.classList.contains('is-open')) return;
if (e.key === 'Escape') closeLightbox();
else if (e.key === 'ArrowLeft') go(-1);
else if (e.key === 'ArrowRight') go(1);
});

var touchStartX = 0, touchStartY = 0;
document.addEventListener('touchstart', function (e) {
if (!state.box || !state.box.classList.contains('is-open')) return;
if (!e.target.closest('.lier-lightbox-inner')) return;
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}, { passive: true });

document.addEventListener('touchend', function (e) {
if (!state.box || !state.box.classList.contains('is-open')) return;
var dx = e.changedTouches[0].clientX - touchStartX;
var dy = e.changedTouches[0].clientY - touchStartY;
if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) {
go(dx < 0 ? 1 : -1);
}
}, { passive: true });
})();
</script>
<?php
}
add_action( 'wp_footer', 'lier_acf_image_gallery_assets' );
}

使用说明

基础用法(显示图片名称):

1
[lier_acf_image_gallery name="lier_product_single_page_detail_image"]

隐藏图片名称:

1
[lier_acf_image_gallery name="lier_product_single_page_detail_image" caption="no"]

可选参数:

参数 默认值 说明
name 必填 ACF 多图字段名
size large 网格缩略图尺寸(thumbnail/medium/large/full)
class 空 追加自定义容器 class
post_id 当前文章 指定文章 ID
caption yes no/false/0 隐藏名称

方案包含的功能

  • 响应式网格:桌面多列、平板 3 列、手机 2 列,4:3 裁切整齐
  • 左下角迷你名称,超长省略,caption="no" 可整体隐藏
  • 点击放大灯箱:原图查看,移动端左右留白不贴边
  • 灯箱上一张/下一张:桌面图片两侧按钮,移动端底部居中;支持键盘 ← →、Esc 关闭、手机左右滑动切换
  • 点遮罩空白或关闭按钮即关,打开锁滚动
  • 防冲突:所有 class/函数/全局变量均 lier- 前缀,脚本 window.lierGalleryInit 防重复注入,样式限定作用域,同页多画廊互不干扰
  • 图片懒加载,网格用 large、灯箱用 full,兼顾速度与清晰度

直接整段替换原文件即可。