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

Lại Đình Cường

Tài liệu thiết kế website

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;
0 0 đánh giá
Đánh giá bài viết
Theo dõi
Thông báo của
guest
0 Góp ý
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất