Làm slide hình ảnh chạy sang trái phải đơn giản

Trong ví dụ này, mình có hơn 10 hình ảnh hiển thị theo chiều ngang, trong màn hình chỉ có thể hiển thị 10 hình ảnh mà thôi, hoặc thậm chí hiển thị ít hơn. Những hình ảnh còn lại sẽ nằm bên ngoài container, để xem được các hình ẩn này mình sẽ dùng 2 nút previous và next để chạy theo dạng slide.

Bên dưới là đoạn mã PHP và HTML ví dụ:

<?php
defined( 'ABSPATH' ) || exit;

if ( is_active_sidebar( 'home_banners' ) ) {
    ?>
    <div class="section home-banners">
        <div class="container">
            <div class="slider-box">
                <div class="banners d-flex no-wrap overflow-auto scrollbar-hide scroll-smooth">
                    <?php dynamic_sidebar( 'home_banners' ); ?>
                </div>
                <div class="absolute top-[50%] left-1 translate-y-[-50%] prev controls">
                    <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeSmall !bg-white mui-style-1j7qk7u"
                            tabindex="0" type="button">
                        <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-style-19xldyr" focusable="false"
                             aria-hidden="true" viewBox="0 0 24 24" data-testid="ArrowBackIosNewIcon">
                            <path d="M17.77 3.77 16 2 6 12l10 10 1.77-1.77L9.54 12z"></path>
                        </svg>
                        <span class="MuiTouchRipple-root mui-style-w0pj6f"></span></button>
                </div>
                <div class="absolute top-[50%] right-1 translate-y-[-50%] next controls">
                    <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeSmall !bg-white mui-style-1j7qk7u"
                            tabindex="0" type="button">
                        <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium mui-style-19xldyr" focusable="false"
                             aria-hidden="true" viewBox="0 0 24 24" data-testid="ArrowForwardIosIcon">
                            <path d="M6.23 20.23 8 22l10-10L8 2 6.23 3.77 14.46 12z"></path>
                        </svg>
                        <span class="MuiTouchRipple-root mui-style-w0pj6f"></span></button>
                </div>
            </div>
        </div>
    </div>
    <?php
}

Đoạn code trên mình sẽ hiện thị các widget chứa hình ảnh vào thẻ div có class là banners.

Ảnh demo carousel đơn giản bằng Javascript
Ảnh demo carousel đơn giản bằng Javascript

Bên dưới là đoạn Javascript để xử lý các sự kiện click nút chạy sang phải và trái.

window.HOCWP_THEME = window.HOCWP_THEME || {};

jQuery(document).ready(function ($) {
    const BODY = $("body");

    HOCWP_THEME.FRONTEND = {
        init: function () {
            this.homeBanners();
        },
        homeBanners: function () {
            let offset = null,
                offsetLeft = null,
                offsetRight = null;

            function update_carousel(box, banners, margin_left = 0, end = false) {
                if (0 !== margin_left) {
                    margin_left += "px";
                }

                banners.animate({marginLeft: margin_left}, "slow");

                if (end) {
                    box.addClass("end");
                    box.removeClass("begin");
                } else {
                    box.addClass("begin");
                    box.removeClass("end");
                }

                banners.removeClass("change-margin");
                banners.attr("data-change-margin", 0);
            }

            function banners_carousel(element, init = false) {
                let box = element.closest(".slider-box"),
                    banners = box.find(".banners"),
                    gap = parseInt(banners.css("gap")),
                    widgets = banners.find(".widget"),
                    widget = widgets.first(),
                    step = widget.width(),
                    width = (step * widgets.length) + (gap * widgets.length) - gap,
                    hidden = width - box.width(),
                    direction,
                    margin;

                if (!offset) {
                    // Current offset left
                    offset = banners.offset().left;
                    // Keep offset left
                    offsetLeft = offset;
                    offsetRight = offsetLeft + box.width();
                }

                if (init) {
                    box.attr("data-offset-left", offsetLeft);
                    box.attr("data-current-offset", offset);
                    box.attr("data-offset-right", offsetRight);
                    box.attr("width", width);
                    box.attr("data-outside", hidden);
                    update_carousel(box, banners);

                    return {
                        step: offset,
                        direction: "default"
                    };
                }

                if (offset >= offsetLeft) {
                    box.addClass("begin");
                }

                if (element.hasClass("prev")) {
                    if (!box.hasClass("begin")) {
                        offset += step;
                        margin = "+";
                    }

                    direction = "prev";
                } else {
                    if (!box.hasClass("end")) {
                        offset -= step;
                        margin = "-";
                    }

                    direction = "next";
                }

                if (margin) {
                    let marginLeft = parseInt(banners.css("margin-left"));

                    if (banners.hasClass("change-margin")) {
                        step -= parseInt(banners.attr("data-change-margin"));
                        banners.removeClass("change-margin");
                        banners.attr("data-change-margin", 0);
                    }

                    banners.animate({marginLeft: margin + "=" + step + "px"}, {
                        duration: 500,
                        complete: function () {
                            marginLeft = parseInt(banners.css("margin-left"));

                            box.attr("data-margin", marginLeft);

                            if (0 === marginLeft || -0 === marginLeft || marginLeft === (gap - 2)) {
                                update_carousel(box, banners);
                            } else if (0 > marginLeft && (hidden + marginLeft) <= 0) {
                                // Fix last item has no gap
                                banners.animate({marginLeft: "+=" + gap + "px"}, "slow");
                                banners.attr("data-change-margin", gap);
                                banners.addClass("change-margin");

                                box.addClass("end");
                                box.removeClass("begin");
                            } else {
                                box.removeClass("begin end");
                            }

                            element.removeClass("waiting");

                            if ("prev" === direction && (Math.abs(marginLeft) < step || marginLeft > 0)) {
                                update_carousel(box, banners);
                            } else if ("next" === direction) {
                                if (Math.abs(marginLeft) > hidden) {
                                    update_carousel(box, banners);
                                } else if (Math.abs(Math.abs(marginLeft) - hidden) < step) {
                                    update_carousel(box, banners, -hidden, true);
                                }
                            }
                        }
                    });
                }

                box.attr("data-current-offset", offset);

                setTimeout(function () {
                    element.removeClass("waiting");
                }, 1000);

                return {
                    step: offset,
                    direction: direction
                };
            }

            banners_carousel(BODY.find(".home-banners .controls"), true);

            BODY.on("click", ".home-banners .controls", function (e) {
                e.preventDefault();
                let that = this,
                    element = $(that);

                if (!element.hasClass("waiting")) {
                    element.addClass("waiting");
                    banners_carousel(element);
                }
            });
        }
    };

    HOCWP_THEME.FRONTEND.init();
});

Với đoạn mã JS bên trên chúng ta chỉ có thể áp dụng cho các widget có cùng chiều rộng. Vì công thức tính chiều rộng lúc này sẽ lấy chiều rộng của 1 widget đầu tiên và nhân cho số lượng widget sẵn có. Để đoạn code chạy đúng hơn, mình cập nhật lại một chút để tính chiều rộng của các widget:

let box = element.closest(".slider-box"),
    banners = box.find(".banners"),
    gap = parseInt(banners.css("gap")),
    widgets = banners.find(".widget"),
    widget = widgets.first(),
    step = widget.width(),
    width = 0;

widgets.each(function () {
    let widgetWidth = $(this).width();

    if (step > widgetWidth) {
        step = widgetWidth;
    }

    width += widgetWidth + gap;
});

width -= gap;

let hidden = width - box.width(),
    direction,
    margin;
5/5 - (1 bình chọn)

Lại Đình Cường

Tôi làm quen và phát triển WordPress từ năm 2008, cho đến nay thì đã có hơn 13 năm kinh nghiệm, thật không thể tin được. Tôi có đam mê và dành nhiều thời gian làm việc với WordPress mỗi ngày, hiện tại tôi đang phát triển giao diện và plugin cho WordPress.

Nếu bạn đang cần người làm trang web bằng WordPress? Hãy liên hệ với tôi ngay để được tư vấn nhé.