多图轮播功能实现

72 阅读1分钟

实现一个多图轮播功能

  • 如图

image.png

js部分

class PerfectCarousel {
    constructor(containerId, data, options = {}) {
        // 初始化配置
        this.options = {
            slideWidth: 295,
            slideHeight: 240,
            gap: 10,
            slidesToShow: 4,
            autoPlay: true,
            interval: 3000,
            resumeDelay: 5000,
            ...options
        };

        // 数据克隆
        this.originalData = data;
        this.data = [...data, ...data, ...data];
        this.currentIndex = data.length;

        // 状态控制
        this.isPaused = false;
        this.userActionTimer = null;
        this.isPausedByHover = false;

        this.init(containerId);
        if (this.options.autoPlay) this.startAutoPlay();
    }

    init(containerId) {
        // 容器初始化
        this.container = document.getElementById(containerId);
        if (!this.container) {
            console.error(`Container #${containerId} not found`);
            return;
        }

        // 清空容器
        this.container.innerHTML = '';
        this.container.style.position = 'relative';
        this.container.style.width = `${this.options.slideWidth * this.options.slidesToShow +
        this.options.gap * (this.options.slidesToShow - 1)}px`;
        this.container.style.height = `${this.options.slideHeight}px`;
        this.container.style.overflow = 'hidden';

        // 创建轨道
        this.track = document.createElement('div');
        this.track.className = 'pc-track';
        this.track.style.display = 'flex';
        this.track.style.height = '100%';
        this.track.style.willChange = 'transform';
        this.container.appendChild(this.track);

        // 创建轮播项
        this.data.forEach((item, i) => {
            const slide = document.createElement('div');
            slide.className = 'pc-slide';
            slide.style.width = `${this.options.slideWidth}px`;
            slide.style.marginRight = `${this.options.gap}px`;
            slide.style.flexShrink = '0';
            slide.style.position = 'relative';

            const link = document.createElement('a');
            link.className = 'pc-link';
            link.href = item.target || '#';
            link.target = '_blank';

            const img = document.createElement('img');
            img.className = 'pc-image';
            img.src = item.url;
            img.alt = item.title;
            img.style.width = '100%';
            img.style.height = '100%';
            img.style.objectFit = 'cover';
            img.style.cursor = 'pointer';

            const caption = document.createElement('div');
            caption.className = 'pc-caption';
            caption.textContent = item.title;
            caption.style.position = 'absolute';
            caption.style.bottom = '0';
            caption.style.width = '100%';
            caption.style.height = '40px';
            caption.style.background = 'rgba(0,0,0,0.6)';
            caption.style.color = 'white';
            caption.style.display = 'flex';
            caption.style.alignItems = 'center';
            caption.style.justifyContent = 'center';

            link.appendChild(img);
            slide.appendChild(link);
            slide.appendChild(caption);
            this.track.appendChild(slide);
        });

        // 创建导航按钮
        this.prevBtn = document.createElement('button');
        this.prevBtn.className = 'pc-btn pc-prev';
        this.prevBtn.innerHTML = '❮';
        this.prevBtn.style.position = 'absolute';
        this.prevBtn.style.top = '50%';
        this.prevBtn.style.left = '0';
        this.prevBtn.style.transform = 'translateY(-50%)';
        this.prevBtn.style.width = '45px';
        this.prevBtn.style.height = '60px';
        this.prevBtn.style.background = 'rgba(0,0,0,0.6)';
        this.prevBtn.style.color = 'white';
        this.prevBtn.style.border = 'none';
        this.prevBtn.style.fontSize = '24px';
        this.prevBtn.style.cursor = 'pointer';
        this.prevBtn.style.zIndex = '10';
        this.container.appendChild(this.prevBtn);

        this.nextBtn = document.createElement('button');
        this.nextBtn.className = 'pc-btn pc-next';
        this.nextBtn.innerHTML = '❯';
        this.nextBtn.style.position = 'absolute';
        this.nextBtn.style.top = '50%';
        this.nextBtn.style.right = '0';
        this.nextBtn.style.transform = 'translateY(-50%)';
        this.nextBtn.style.width = '45px';
        this.nextBtn.style.height = '60px';
        this.nextBtn.style.background = 'rgba(0,0,0,0.6)';
        this.nextBtn.style.color = 'white';
        this.nextBtn.style.border = 'none';
        this.nextBtn.style.fontSize = '24px';
        this.nextBtn.style.cursor = 'pointer';
        this.nextBtn.style.zIndex = '10';
        this.container.appendChild(this.nextBtn);

        // 事件绑定
        this.prevBtn.addEventListener('click', () => {
            this.handleUserAction();
            this.prev();
        });

        this.nextBtn.addEventListener('click', () => {
            this.handleUserAction();
            this.next();
        });

        this.container.addEventListener('mouseenter', () => {
            this.isPausedByHover = true;
            this.pause();
        });

        this.container.addEventListener('mouseleave', () => {
            this.isPausedByHover = false;
            this.resume();
        });

        this.track.addEventListener('transitionend', () => this.checkPosition());

        // 初始位置
        this.updatePosition(true);
    }

    handleUserAction() {
        this.pause();

        clearTimeout(this.userActionTimer);
        this.userActionTimer = setTimeout(() => {
            if (!this.isPausedByHover) this.resume();
        }, this.options.resumeDelay);
    }

    pause() {
        if (!this.isPaused) {
            clearInterval(this.autoPlayInterval);
            this.isPaused = true;
        }
    }

    resume() {
        if (this.isPaused) {
            this.startAutoPlay();
            this.isPaused = false;
        }
    }

    startAutoPlay() {
        clearInterval(this.autoPlayInterval);
        this.autoPlayInterval = setInterval(() => {
            if (!this.isPaused) this.next();
        }, this.options.interval);
    }

    next() {
        this.currentIndex++;
        this.updatePosition();
    }

    prev() {
        this.currentIndex--;
        this.updatePosition();
    }

    updatePosition(instant = false) {
        const offset = -this.currentIndex * (this.options.slideWidth + this.options.gap);
        this.track.style.transition = instant ? 'none' : 'transform 0.5s ease';
        this.track.style.transform = `translateX(${offset}px)`;
    }

    checkPosition() {
        if (this.currentIndex <= 0) {
            this.currentIndex = this.originalData.length;
            this.updatePosition(true);
        }
        else if (this.currentIndex >= this.originalData.length * 2) {
            this.currentIndex = this.originalData.length;
            this.updatePosition(true);
        }
    }
}

css部分

.pc-container {
    position: relative;
    width: 1200px; /* 295*4 + 10*3 */
    height: 240px;
    overflow: hidden;
    margin: 0 auto;
}

.pc-track {
    display: flex;
    height: 100%;
    will-change: transform;
}

.pc-slide {
    flex-shrink: 0;
    position: relative;
}

.pc-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    cursor: pointer;
}

.pc-caption {
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 40px;
    background: rgba(0,0,0,0.6);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
}

.pc-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 45px;
    height: 60px;
    background: rgba(0,0,0,0.6);
    color: white;
    border: none;
    font-size: 24px;
    cursor: pointer;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
}

.pc-prev { left: 0; }
.pc-next { right: 0; }

.pc-btn:hover {
    background: rgba(0,0,0,0.8);
    transform: translateY(-50%) scale(1.05);
}

.pc-container:hover .pc-image {
    opacity: 0.9;
    transition: opacity 0.3s;
}

使用方式

<div id="carouseContainer" class="pc-container"></div>
const myCarousel = new PerfectCarousel('carouseContainer', carouselData, {
    autoPlay: true,
    interval: 3000,
    showButtons: true
});
  • carouselData数据格式

    [{url:'图片路径',title:'轮播标题',target:'点击图片的跳转链接'},...]