原生JS 轮播图组件(完整可复用、封装式、自动轮播+指示器+左右切换)

5 阅读2分钟

零依赖、纯原生 JS + HTML + CSS,组件化封装,多处复用、可配置参数、结构独立,直接复制打开即用。

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>原生JS 轮播图组件</title>
  <style>
    /* 轮播容器样式 */
    .carousel-wrap {
      position: relative;
      width: 600px;
      height: 340px;
      overflow: hidden;
      margin: 50px auto;
    }
    .carousel-list {
      display: flex;
      height: 100%;
      transition: transform 0.5s ease;
      list-style: none;
      margin: 0;
      padding: 0;
    }
    .carousel-item {
      min-width: 100%;
      height: 100%;
    }
    .carousel-item img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    /* 左右箭头 */
    .carousel-btn {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 40px;
      height: 40px;
      background: rgba(0,0,0,.3);
      color: #fff;
      text-align: center;
      line-height: 40px;
      font-size: 20px;
      cursor: pointer;
      border: none;
      z-index: 10;
    }
    .prev { left: 10px; }
    .next { right: 10px; }
    /* 指示器 */
    .carousel-indicator {
      position: absolute;
      bottom: 20px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
      gap: 8px;
      list-style: none;
      padding: 0;
      margin: 0;
    }
    .indicator-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: rgba(255,255,255,.5);
      cursor: pointer;
    }
    .indicator-dot.active {
      background: #fff;
    }
  </style>
</head>
<body>
  <!-- 轮播挂载容器 -->
  <div id="carouselBox"></div>

  <script>
    /**
     * 原生轮播图组件 - 构造函数封装
     * @param {String} selector 挂载容器选择器
     * @param {Object} options 配置项
     */
    function Carousel(selector, options) {
      // 默认配置
      const defaultOpts = {
        imgList: [],        // 图片数组
        autoPlay: true,     // 是否自动播放
        interval: 3000,     // 轮播间隔(ms)
        showArrow: true,    // 是否显示左右箭头
        showIndicator: true // 是否显示指示器
      };

      // 合并配置
      this.opts = Object.assign({}, defaultOpts, options);
      this.container = document.querySelector(selector);
      this.index = 0;      // 当前索引
      this.timer = null;   // 定时器
      this.init();
    }

    // 初始化渲染结构
    Carousel.prototype.init = function() {
      this.render();
      this.bindEvent();
      // 自动播放
      if(this.opts.autoPlay) {
        this.startAutoPlay();
      }
    };

    // 渲染DOM结构
    Carousel.prototype.render = function() {
      const { imgList, showArrow, showIndicator } = this.opts;
      let html = `
        <div class="carousel-wrap">
          <ul class="carousel-list">
      `;
      // 渲染图片
      imgList.forEach(item => {
        html += `<li class="carousel-item"><img src="${item}" alt="轮播图"></li>`;
      });
      html += `</ul>`;

      // 左右箭头
      if(showArrow) {
        html += `
          <button class="carousel-btn prev">&lt;</button>
          <button class="carousel-btn next">&gt;</button>
        `;
      }

      // 指示器
      if(showIndicator) {
        html += `<ul class="carousel-indicator">`;
        imgList.forEach((_, i) => {
          html += `<li class="indicator-dot ${i === 0 ? 'active' : ''}" data-index="${i}"></li>`;
        });
        html += `</ul>`;
      }
      html += `</div>`;
      this.container.innerHTML = html;

      // 缓存DOM
      this.list = this.container.querySelector('.carousel-list');
      this.dots = this.container.querySelectorAll('.indicator-dot');
      this.prevBtn = this.container.querySelector('.prev');
      this.nextBtn = this.container.querySelector('.next');
    };

    // 切换图片核心方法
    Carousel.prototype.switchImg = function() {
      const { imgList } = this.opts;
      // 移动容器
      this.list.style.transform = `translateX(${-this.index * 600}px)`;
      // 更新指示器激活态
      this.dots.forEach(dot => dot.classList.remove('active'));
      if(this.dots[this.index]) {
        this.dots[this.index].classList.add('active');
      }
    };

    // 下一张
    Carousel.prototype.next = function() {
      const { imgList } = this.opts;
      this.index++;
      if(this.index >= imgList.length) this.index = 0;
      this.switchImg();
    };

    // 上一张
    Carousel.prototype.prev = function() {
      const { imgList } = this.opts;
      this.index--;
      if(this.index < 0) this.index = imgList.length - 1;
      this.switchImg();
    };

    // 绑定事件
    Carousel.prototype.bindEvent = function() {
      // 左右箭头
      this.nextBtn && this.nextBtn.addEventListener('click', () => this.next());
      this.prevBtn && this.prevBtn.addEventListener('click', () => this.prev());

      // 指示器点击
      this.dots.forEach(dot => {
        dot.addEventListener('click', (e) => {
          this.index = Number(e.target.dataset.index);
          this.switchImg();
        });
      });

      // 鼠标悬停暂停、离开继续
      const wrap = this.container.querySelector('.carousel-wrap');
      wrap.addEventListener('mouseenter', () => this.stopAutoPlay());
      wrap.addEventListener('mouseleave', () => this.startAutoPlay());
    };

    // 开启自动播放
    Carousel.prototype.startAutoPlay = function() {
      this.timer = setInterval(() => {
        this.next();
      }, this.opts.interval);
    };

    // 暂停自动播放
    Carousel.prototype.stopAutoPlay = function() {
      clearInterval(this.timer);
    };


    // ========== 使用组件 ==========
    new Carousel('#carouselBox', {
      imgList: [
        'https://picsum.photos/id/237/600/340',
        'https://picsum.photos/id/1/600/340',
        'https://picsum.photos/id/10/600/340'
      ],
      autoPlay: true,
      interval: 3000,
      showArrow: true,
      showIndicator: true
    });
  </script>
</body>
</html>

组件核心特性

  1. 完全组件化封装 通过构造函数 Carousel 封装,支持多处页面多次实例化,互不冲突。
  2. 可配置项
  • imgList:轮播图片地址数组
  • autoPlay:是否自动播放
  • interval:自动切换间隔
  • showArrow:是否显示左右切换箭头
  • showIndicator:是否显示底部小圆点指示器
  1. 交互完整
  • 左右按钮切换
  • 底部指示器点击跳转
  • 自动轮播 + 鼠标悬停暂停
  • 无缝循环轮播
  1. 样式独立 CSS 隔离,不会污染全局样式。

快速复用方式

  1. 新建容器:<div id="box2"></div>
  2. 直接 new 实例:
new Carousel('#box2', {
  imgList: ['图1','图2','图3'],
  interval: 4000
})