You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vanilla JS无限循环轮播动画异常问题求助

无限循环轮播组件动画异常问题

我用原生JS实现了无限循环轮播组件,基础功能已完成,但存在动画异常:

  • 滑动到轮播末尾时会直接跳回首张卡片
  • 点击或拖拽首张卡片向前无法切换到最后一张,而是直接跳转

预期效果:

  • 从最后一张滑动时,首张卡片无缝衔接在其后
  • 首张卡片向前滑动时,最后一张卡片无缝衔接在其前

HTML代码

<div class="container layout--fluid">
    <div id="bannerCarousel" draggable="true" class="container">
    <div class="row" ondragstart="return false;" ondrop="return false;">
        <div class="col-md-6 image-content" ondragstart="return false;" ondrop="return false;">
            <img src="https://picsum.photos/200/300" alt="Becel" />

        </div>
        <div class="col-md-6 text-content" ondragstart="return false;" ondrop="return false;">
            <div>
                <h1>Title comes here</h1>
                <p>Description comes here</p>
                <a href="#" class="btn btn-primary">Read More</a>
            </div>

        </div>
    </div>
    <div class="row" ondragstart="return false;" ondrop="return false;">
        <div class="col-md-6 image-content" ondragstart="return false;" ondrop="return false;">
            <img src="https://picsum.photos/200/301" alt="Blueband" />

        </div>
        <div class="col-md-6 text-content" ondragstart="return false;" ondrop="return false;">
            <div>
                <h1>Title comes here</h1>
                <p>Description comes here</p>
                <a href="#" class="btn btn-primary">Read More</a>
            </div>

        </div>
    </div>
    <div class="row" ondragstart="return false;" ondrop="return false;">
        <div class="col-md-6 image-content" ondragstart="return false;" ondrop="return false;">
            <img src="https://picsum.photos/200/303" alt="Country Crock" />

        </div>
        <div class="col-md-6 text-content" ondragstart="return false;" ondrop="return false;">
            <div>
                <h1>Title comes here</h1>
                <p>Description comes here</p>
                <a href="#" class="btn btn-primary">Read More</a>
            </div>

        </div>
    </div>
    <div class="row" ondragstart="return false;" ondrop="return false;">
        <div class="col-md-6 image-content" ondragstart="return false;" ondrop="return false;">
            <img src="https://picsum.photos/200/306" alt="Country Crock" />

        </div>
        <div class="col-md-6 text-content" ondragstart="return false;" ondrop="return false;">
            <div>
                <h1>Title comes here</h1>
                <p>Description comes here</p>
                <a href="#" class="btn btn-primary">Read More</a>
            </div>

        </div>
    </div>
</div>
</div>

CSS代码

body {
    margin: 0px;
    padding: 0px;
}

.container {
    width: 100%;
    background-color: #0d3b30;
    position: relative;
    overflow: hidden;
}

#bannerCarousel {
    display: flex;
    flex-wrap: nowrap;
    position: relative;
    transition: all 1.5s ease;
    -ms-overflow-style: none;
    /* IE and Edge */
    scrollbar-width: none;
    /* Firefox */
    overflow-x: scroll;
    cursor: grab;
    @media only screen and (max-width:900px) {
        gap: 0px;
       }

}

#bannerCarousel::-webkit-scrollbar {
    display: none;
}

#bannerCarousel>.row {
    display: grid;
    grid-template-columns: repeat(2, 50%);
    gap: 20px;
    flex: 0 0 85%;

    @media only screen and (max-width:900px) {
        grid-template-columns: repeat(1, 100%);
        flex: 0 0 65%;
    }
}

#bannerCarousel .image-content img {
    width: 100%;
    height: auto;
    max-width: 550px;
}

#bannerCarousel .text-content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 20px;
    text-align: left;
    
    @media only screen and (max-width:900px) {
     text-align: center;
     padding-bottom: 60px;
    }
}

.text-content h1 {
    color: #fff;
    font-family: var();
    font-size: 48px;
    font-style: normal;
    font-weight: 400;
    line-height: normal;
    margin-top: 0px;
    margin-bottom: 15px;
}

.text-content p {
    color: #fff;

    /* D-UpWir-Subheading */
    font-family: sans-serif;
    font-size: 18px;
    font-style: normal;
    font-weight: 700;
    line-height: 28px;
    margin-top: 0px;
    /* 155.556% */
}

.text-content a {
    display: flex;
    padding: 14px 40px 15px 40px;
    align-items: center;
    justify-content: center;
    gap: 10px;
    border-radius: 100px;
    background: var(--BB-Blue, #151E8A);
    max-width: 200px;
    box-sizing: border-box;
    color: #fff;
    text-decoration: none;
    font-size: 19px;
    font-weight: 700;
    @media only screen and (max-width:900px) {
       margin: 0 auto;
       }
}

.bullet-container {
    position: absolute;
    bottom: 20px;
    left: 50%;
    display: flex;
    flex-wrap: nowrap;
    gap: 10px;
    transform: translateX(-50%);
}

.bullet-container .bullet {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background-color: #fff;
    cursor: pointer;
}
.bullet-container .bullet.active{
  background-color: red;
}

JavaScript代码

let container = document.getElementById("bannerCarousel");
let scrollElement = container.querySelector(".row");
let scrollCount = container.children.length;



//container.querySelector('img').ondragstart = function() { return false; };
class BannerCarousel {
    constructor(container, child) {
        this.slideContainer = container;
        this.slides = child;
        this.length = child.length;
        this.slideWidth = child[0].clientWidth;
        this.dragStart = 0;
        this.curActive = 0;
    }

    move(index) {
        let scrollValue = parseFloat(this.slideWidth) * index;
        if (screen.width <= 900) {
            scrollValue = parseFloat(this.slideWidth) * index - 60;
        }
        console.log(scrollValue);
        this.slideContainer.scroll({
            left: scrollValue,
            top: 0,
            behavior: 'smooth'
        })

    }

    prev() {
        this.curActive++;
        if (this.curActive >= this.length) {
            this.curActive = 0;
        }
        this.makeBulletActive(this.curActive);
        this.makeSlideActive(this.curActive);
        this.move(this.curActive)

    }

    next() {
        this.curActive--;
        if (this.curActive < 0) {
            this.curActive = this.length - 1;
        }
        this.makeBulletActive(this.curActive);
        this.makeSlideActive(this.curActive);
        this.move(this.curActive);
    }

    addDragEvent() {
        let _this = this;
        this.slideContainer.addEventListener("dragstart", function (event) {
            console.log("Drag start");
            _this.dragStart = event.clientX;
        })
        this.slideContainer.addEventListener("dragend", function (event) {
            console.log("Drag end");
            let end = event.clientX;
            let dif = end - _this.dragStart;
            console.log(dif);
            if (dif < 0) {
                _this.prev(_this.curActive)
                console.log("Go right");
            }
            else {
                _this.next(_this.curActive);
                console.log("Go left")
            }
        })

        this.slideContainer.addEventListener("touchstart", function (event) {
            _this.dragStart = event.touches[0].clientX;
        })
        this.slideContainer.addEventListener("touchend", function (event) {
            let end = event.changedTouches[0].clientX;
            let dif = end - _this.dragStart;
            if (dif < 0) {
                //prev();
                console.log("Go right");
            }
            else {
                //next();
                console.log("Go left");
            }
        })
    }
    getChildElementIndex(node) {
        return Array.prototype.indexOf.call(node.parentNode.children, node);
    }

    makeBulletActive(target) {
        let container = document.querySelector(".bullet-container");
        let bullets = container.querySelectorAll("span");
        console.log(bullets);
        bullets.forEach((ele, index) => {
            ele.classList.toggle("active", index === target)
        })
    }
    makeSlideActive(target) {
        this.slides.forEach((ele, index) => {
            ele.classList.toggle("active", index === target)
        })
    }

    addBullet() {
        let bulletContainer = document.createElement("div");
        bulletContainer.className = "bullet-container";
        for (let i = 0; i < this.length; i++) {
            let bullet = document.createElement("span");
            bullet.className = "bullet";
            bulletContainer.appendChild(bullet);
        }
        this.slideContainer.parentElement.appendChild(bulletContainer);
    }
    addBulletClickEvent() {
        const _this = this;
        document.addEventListener("click", function (e) {
            if (e.target.className === "bullet") {
                let index = _this.getChildElementIndex(e.target);
                let scrollValue = parseFloat(_this.slideWidth) * index;
                _this.curActive = index;
                _this.makeBulletActive(index);
                _this.makeSlideActive(index);
                if (screen.width <= 900) {
                    scrollValue = parseFloat(_this.slideWidth) * index - 60;
                }
                console.log(scrollValue);
                _this.slideContainer.scroll({
                    left: scrollValue,
                    top: 0,
                    behavior: 'smooth'
                })
            }
        })
    }
}

let new_carousel = new BannerCarousel(container, container.querySelectorAll(".row"));
new_carousel.addBullet();
new_carousel.addBulletClickEvent();
new_carousel.makeBulletActive(new_carousel.curActive);
new_carousel.makeSlideActive(new_carousel.curActive);
new_carousel.addDragEvent();

内容的提问来源于stack exchange,提问作者Pranay kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 13:09:56