Полезные статьи для
WordPress-разработчиков

Карусель с миниатюрами для галереи в Elementor на основе ACF/SCF

Карусель с миниатюрами для галереи в Elementor на основе ACF/SCF

Приветствую, коллеги! 

Elementor — мощный инструмент, но иногда его штатных возможностей не хватает. Например, когда нужно вывести галерею изображений из произвольных записей в виде карусели с миниатюрами. Я столкнулся с этой задачей и нашёл решение — шорткод на основе Swiper.js, который работает с полями ACF или SCF.

Проблема

В проекте нужно было:

  • Выводить галерею для каждого элемента произвольного типа записей
  • Использовать поле типа «Галерея» из ACF/SCF
  • Отображать карусель с основным слайдером и миниатюрами внизу
  • Чтобы это работало в цикле Elementor (например, в виджете «Loop Grid»)

Штатный виджет «Галерея» Elementor не умеет подтягивать изображения из произвольных полей. А виджет «Шорткод» отлично справляется с кастомными решениями.


Решение

1. Подключаем Swiper.js

Добавьте в functions.php подключение библиотеки Swiper (если она ещё не подключена):

// Подключаем Swiper.js и CSS
add_action('wp_enqueue_scripts', function() {
    if (wp_script_is('swiper-js', 'enqueued')) {
        return;
    }

    wp_enqueue_style(
        'swiper-css',
        'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css',
        [],
        '11'
    );

    wp_enqueue_script(
        'swiper-js',
        'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js',
        [],
        '11',
        true
    );
});

2. Шорткод для галереи

Там же, в functions.php, добавляем сам шорткод:

// Шорткод галереи: [gallery_swiper]
function acf_gallery_swiper_shortcode($atts) {
    $atts = shortcode_atts([
        'field'   => 'galereya_proekta', // имя поля галереи
        'post_id' => get_the_ID(),
    ], $atts);

    static $counter = 0;
    $counter++;
    $unique_id = 'swiper-' . $atts['post_id'] . '-' . $counter;

    $images = get_field($atts['field'], $atts['post_id']);
    if (!$images || !is_array($images)) {
        return '';
    }

    ob_start();
    ?>
    <div class="custom-swiper-gallery" id="<?php echo esc_attr($unique_id); ?>">
        <!-- Основной слайдер -->
        <div class="swiper <?php echo esc_attr($unique_id); ?>-main swiper-main">
            <div class="swiper-wrapper">
                <?php foreach ($images as $image): ?>
                    <div class="swiper-slide">
                        <img src="<?php echo esc_url($image['url']); ?>"
                             alt="<?php echo esc_attr($image['alt'] ?? ''); ?>">
                    </div>
                <?php endforeach; ?>
            </div>
            <div class="swiper-button-next"></div>
            <div class="swiper-button-prev"></div>
        </div>

        <!-- Миниатюры -->
        <div class="swiper <?php echo esc_attr($unique_id); ?>-thumbs swiper-thumbs">
            <div class="swiper-wrapper">
                <?php foreach ($images as $image): ?>
                    <div class="swiper-slide">
                        <img src="<?php echo esc_url($image['sizes']['thumbnail'] ?? $image['url']); ?>"
                             alt="<?php echo esc_attr($image['alt'] ?? ''); ?>">
                    </div>
                <?php endforeach; ?>
            </div>
        </div>
    </div>

    <script>
    document.addEventListener('DOMContentLoaded', function() {
        const thumbsSwiper = new Swiper('.<?php echo esc_js($unique_id); ?>-thumbs', {
            loop: false,
            spaceBetween: 10,
            slidesPerView: 'auto',
            watchSlidesProgress: true,
            freeMode: true
        });

        const mainSwiper = new Swiper('.<?php echo esc_js($unique_id); ?>-main', {
            loop: true,
            spaceBetween: 10,
            navigation: {
                nextEl: '.<?php echo esc_js($unique_id); ?>-main .swiper-button-next',
                prevEl: '.<?php echo esc_js($unique_id); ?>-main .swiper-button-prev',
            },
            thumbs: {
                swiper: thumbsSwiper
            }
        });
    });
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode('gallery_swiper', 'acf_gallery_swiper_shortcode');

3. Стили для карусели

Добавьте CSS в стили темы или в «Дополнительный CSS» Elementor:

/* === Контейнер Loop с галереей === */
.loop-gallery {
    min-width: 0 !important;
    overflow: hidden;
}

.loop-gallery .elementor-widget-shortcode {
    width: 100% !important;
}

/* === Общий контейнер Swiper === */
.custom-swiper-gallery {
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 auto;
    box-sizing: border-box !important;
    overflow: hidden !important;
    position: relative;
}

/* Сброс min-width для родительских элементов */
.elementor-3019 .elementor-element-0aac6e1,
.elementor-3019 .elementor-element-b5403f5,
.elementor-3019 .elementor-element-1148b3e {
    min-width: 0 !important;
}

.custom-swiper-gallery .swiper {
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box !important;
}

/* Основной слайдер */
.custom-swiper-gallery .swiper-main .swiper-wrapper {
    display: flex;
}

.custom-swiper-gallery .swiper-main .swiper-slide {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100% !important;
    max-width: 100% !important;
    margin-right: 10px;
}

.custom-swiper-gallery .swiper-main .swiper-slide img {
    width: 100%;
    max-width: 100%;
    height: auto;
    max-height: 380px;
    object-fit: cover;
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.18);
}

/* === Миниатюры === */
.custom-swiper-gallery .swiper-thumbs {
    margin-top: 15px;
    width: 100%;
    max-width: 100%;
    padding: 8px 0;
    box-sizing: border-box;
    white-space: nowrap;
}

.custom-swiper-gallery .swiper-thumbs .swiper-wrapper {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
}

.custom-swiper-gallery .swiper-thumbs .swiper-slide {
    width: auto !important;
    flex: 0 0 auto !important;
    cursor: pointer;
    padding: 4px;
    box-sizing: border-box;
    opacity: 0.6;
    transition: opacity 0.25s ease, transform 0.25s ease;
    display: flex;
    justify-content: center;
    align-items: center;
}

.custom-swiper-gallery .swiper-thumbs .swiper-slide:hover {
    opacity: 0.9;
    transform: translateY(-2px);
}

.custom-swiper-gallery .swiper-thumbs .swiper-slide img {
    width: 100px;
    height: 60px;
    object-fit: cover;
    border-radius: 4px;
    border: 2px solid transparent;
    box-shadow: 0 2px 6px rgba(0,0,0,0.2);
    transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.custom-swiper-gallery .swiper-thumbs .swiper-slide-thumb-active {
    opacity: 1;
}

.custom-swiper-gallery .swiper-thumbs .swiper-slide-thumb-active img,
.custom-swiper-gallery .swiper-thumbs .swiper-slide:hover img {
    border-color: #FEA12A;
    transform: scale(1.03);
}

/* === Цвета темы === */
.elementor-widget-shortcode .custom-swiper-gallery {
    --swiper-theme-color: #FEA12A !important;
    --swiper-navigation-size: 20px !important;
}

/* Кнопки навигации */
.custom-swiper-gallery .swiper-button-next,
.custom-swiper-gallery .swiper-button-prev {
    color: #2C405B;
    background: rgba(255, 255, 255, 0.95);
    border-radius: 50%;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px !important;
    box-shadow: 0 4px 10px rgba(0,0,0,0.25);
    transition: background-color 0.25s ease, color 0.25s ease, transform 0.15s ease;
}

.custom-swiper-gallery .swiper-button-next {
    right: 12px !important;
}
.custom-swiper-gallery .swiper-button-prev {
    left: 12px !important;
}

.custom-swiper-gallery .swiper-button-next:hover,
.custom-swiper-gallery .swiper-button-prev:hover {
    background: #FEA12A;
    color: #ffffff;
    transform: translateY(-1px);
}

/* Адаптив */
@media (max-width: 768px) {
    .custom-swiper-gallery .swiper-main .swiper-slide img {
        max-height: 320px;
    }

    .custom-swiper-gallery .swiper-button-next,
    .custom-swiper-gallery .swiper-button-prev {
        width: 30px;
        height: 30px;
        font-size: 18px !important;
    }

    .custom-swiper-gallery .swiper-thumbs .swiper-slide img {
        width: 80px;
        height: 50px;
    }
}

@media (max-width: 480px) {
    .custom-swiper-gallery .swiper-main .swiper-slide img {
        max-height: 240px;
    }

    .custom-swiper-gallery .swiper-thumbs .swiper-slide img {
        width: 65px;
        height: 42px;
    }
}

Как использовать в Elementor

Шаг 1. Создайте поле галереи в ACF/SCF

  • Тип поля: Галерея
  • Имя поля: например galereya_proekta

Шаг 2. Заполните изображения в записях

Добавьте несколько изображений в поле галереи для тестовой записи.

Шаг 3. Добавьте шорткод в шаблон Elementor

В любом месте используйте виджет «Шорткод» и вставьте:

[gallery_swiper]

Если нужно указать другое имя поля или конкретный ID записи:

[gallery_swiper field="drugoe_pole" post_id="123"]

Шаг 4. Работа в цикле (Loop Grid)

Если используете виджет «Loop Grid» Elementor Pro, шорткод автоматически подхватит текущую запись и выведет её галерею. Для корректной ширины добавьте родительскому контейнеру класс loop-gallery:

.loop-gallery {
    min-width: 0 !important;
    overflow: hidden;
}

Как это работает

  1. Шорткод получает изображения из поля ACF/SCF для текущей записи
  2. Swiper.js создаёт два связанных слайдера: основной и миниатюры
  3. Уникальные ID позволяют использовать несколько галерей на одной странице
  4. Стили обеспечивают корректное отображение в любой раскладке Elementor

Преимущества решения

  • Универсальность — работает с ACF и SCF
  • Множественные галереи — можно использовать несколько на странице
  • Адаптивность — корректно отображается на всех устройствах
  • Кастомизация — легко менять цвета, размеры, поведение
  • Без плагинов — только код и библиотека Swiper

Кастомизация

Изменить цвета:

В CSS найдите #FEA12A и замените на свой цвет.

Изменить размеры миниатюр:

.custom-swiper-gallery .swiper-thumbs .swiper-slide img {
    width: 120px; /* новая ширина */
    height: 70px; /* новая высота */
}

Это решение закрывает потребность в кастомных галереях для произвольных записей там, где Elementor бессилен. Код проверен в реальных проектах и стабильно работает.

Пользуйтесь и адаптируйте под свои задачи!

Последние статьи