我们将创建一个功能齐全的照片轮播组件,具备以下特点:

JavaScript做网页照片轮播
(图片来源网络,侵删)
  • 自动播放
  • 左右箭头手动切换
  • 底部圆点指示器
  • 鼠标悬停时暂停
  • 平滑的过渡动画

使用原生 JavaScript (推荐理解原理)

这种方法不依赖任何外部库,能帮助你最好地理解轮播的底层工作原理。

最终效果预览

实现步骤

第 1 步:HTML 结构

我们创建轮播图所需的 HTML 结构,它需要一个容器,里面包含图片列表、左右箭头和指示器。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">原生 JS 轮播图</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="carousel-container">
        <!-- 轮播图片列表 -->
        <div class="carousel-slide">
            <img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
            <img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
            <img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
            <img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
            <img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
        </div>
        <!-- 左右箭头 -->
        <button class="carousel-btn prev-btn">&#10094;</button>
        <button class="carousel-btn next-btn">&#10095;</button>
        <!-- 底部指示器 -->
        <div class="carousel-indicators"></div>
    </div>
    <script src="script.js"></script>
</body>
</html>

第 2 步:CSS 样式

JavaScript做网页照片轮播
(图片来源网络,侵删)

CSS 的关键在于:

  1. 将所有图片水平排列在一行。
  2. 使用 overflow: hidden 来隐藏容器外的图片,只显示当前图片。
  3. 通过 transform: translateX() 来移动整个图片列表,实现切换效果。
/* style.css */
body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f4f4f4;
}
.carousel-container {
    position: relative;
    width: 800px; /* 根据你的图片尺寸调整 */
    height: 450px; /* 根据你的图片尺寸调整 */
    overflow: hidden;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.carousel-slide {
    display: flex;
    transition: transform 0.5s ease-in-out; /* 平滑过渡效果 */
}
.carousel-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* 确保图片填满容器且不变形 */
}
/* 左右箭头样式 */
.carousel-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    border: none;
    padding: 16px;
    cursor: pointer;
    font-size: 18px;
    border-radius: 50%;
    transition: background-color 0.3s;
}
.carousel-btn:hover {
    background-color: rgba(0, 0, 0, 0.8);
}
.prev-btn {
    left: 10px;
}
.next-btn {
    right: 10px;
}
/* 指示器样式 */
.carousel-indicators {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 10px;
}
.dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition: background-color 0.3s;
}
.dot.active {
    background-color: white;
}

**第 3 步:JavaScript 逻辑 (核心部分)

这是轮播图的大脑,负责处理用户交互和自动播放逻辑。

// script.js
document.addEventListener('DOMContentLoaded', () => {
    const carouselSlide = document.querySelector('.carousel-slide');
    const images = document.querySelectorAll('.carousel-slide img');
    const prevBtn = document.querySelector('.prev-btn');
    const nextBtn = document.querySelector('.next-btn');
    const indicatorsContainer = document.querySelector('.carousel-indicators');
    // 计算图片数量和宽度
    const imageCount = images.length;
    const imageWidth = images[0].clientWidth;
    let counter = 0;
    // 克隆第一张图片并添加到末尾,实现无缝循环
    const firstClone = images[0].cloneNode(true);
    carouselSlide.appendChild(firstClone);
    // 创建底部指示器
    for (let i = 0; i < imageCount; i++) {
        const dot = document.createElement('span');
        dot.classList.add('dot');
        if (i === 0) dot.classList.add('active');
        dot.addEventListener('click', () => {
            counter = i;
            updateCarousel();
            updateIndicators();
        });
        indicatorsContainer.appendChild(dot);
    }
    const dots = document.querySelectorAll('.dot');
    // 更新轮播图位置
    function updateCarousel() {
        carouselSlide.style.transform = `translateX(${-counter * imageWidth}px)`;
    }
    // 更新指示器状态
    function updateIndicators() {
        dots.forEach(dot => dot.classList.remove('active'));
        dots[counter % imageCount].classList.add('active');
    }
    // 点击下一张
    nextBtn.addEventListener('click', () => {
        if (counter >= imageCount) {
            // 如果是克隆的图片,瞬间跳回第一张
            carouselSlide.style.transition = 'none';
            counter = 0;
            updateCarousel();
            // 强制重绘,然后应用过渡效果并移动到第二张
            setTimeout(() => {
                carouselSlide.style.transition = 'transform 0.5s ease-in-out';
                counter++;
                updateCarousel();
                updateIndicators();
            }, 20);
        } else {
            counter++;
            updateCarousel();
            updateIndicators();
        }
    });
    // 点击上一张
    prevBtn.addEventListener('click', () => {
        if (counter === 0) {
            // 如果是第一张,瞬间跳到克隆的那张(最后一张)
            carouselSlide.style.transition = 'none';
            counter = imageCount;
            updateCarousel();
            // 强制重绘,然后应用过渡效果并移动到倒数第二张
            setTimeout(() => {
                carouselSlide.style.transition = 'transform 0.5s ease-in-out';
                counter--;
                updateCarousel();
                updateIndicators();
            }, 20);
        } else {
            counter--;
            updateCarousel();
            updateIndicators();
        }
    });
    // 自动播放功能
    let slideInterval = setInterval(() => {
        nextBtn.click();
    }, 3000); // 每3秒切换一次
    // 鼠标悬停时暂停
    const carouselContainer = document.querySelector('.carousel-container');
    carouselContainer.addEventListener('mouseenter', () => {
        clearInterval(slideInterval);
    });
    // 鼠标离开时继续播放
    carouselContainer.addEventListener('mouseleave', () => {
        slideInterval = setInterval(() => {
            nextBtn.click();
        }, 3000);
    });
});

使用 CSS has() 选择器 (现代、简洁)

如果你的项目环境支持较新的浏览器,可以使用 CSS 的 has() 选择器来实现一个非常简洁的轮播图,无需 JavaScript。

JavaScript做网页照片轮播
(图片来源网络,侵删)

HTML 结构 (与方法一相同)

CSS 样式

/* style.css */
body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f4f4f4;
}
.carousel-container {
    position: relative;
    width: 800px;
    height: 450px;
    overflow: hidden;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.carousel-slide {
    display: flex;
    transition: transform 0.5s ease-in-out;
}
/* 隐藏所有图片,除了当前激活的 */
.carousel-slide img:not(:first-child) {
    display: none;
}
/* 当容器拥有 .active-dot 类时,显示对应的图片 */
/* :nth-child(n+2) 是为了跳过第一张图片,从第二张开始计数 */
.carousel-container:has(.active-dot:nth-child(2)) .carousel-slide img:nth-child(2) { display: block; }
.carousel-container:has(.active-dot:nth-child(3)) .carousel-slide img:nth-child(3) { display: block; }
.carousel-container:has(.active-dot:nth-child(4)) .carousel-slide img:nth-child(4) { display: block; }
.carousel-container:has(.active-dot:nth-child(5)) .carousel-slide img:nth-child(5) { display: block; }
.carousel-container:has(.active-dot:nth-child(6)) .carousel-slide img:nth-child(6) { display: block; }
.carousel-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* 箭头样式 */
.carousel-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    border: none;
    padding: 16px;
    cursor: pointer;
    font-size: 18px;
    border-radius: 50%;
    transition: background-color 0.3s;
    z-index: 10;
}
.carousel-btn:hover {
    background-color: rgba(0, 0, 0, 0.8);
}
.prev-btn {
    left: 10px;
}
.next-btn {
    right: 10px;
}
/* 指示器样式 */
.carousel-indicators {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 10px;
    z-index: 10;
}
.dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition: background-color 0.3s;
}
.dot.active {
    background-color: white;
}

HTML 结构 (需要一些修改)

为了让 CSS has() 能够工作,我们需要在 HTML 中通过 radio 按钮来控制状态。

<!-- index.html -->
<div class="carousel-container">
    <!-- 使用 radio 按钮来控制当前显示的图片 -->
    <input type="radio" name="carousel" id="slide1" checked>
    <input type="radio" name="carousel" id="slide2">
    <input type="radio" name="carousel" id="slide3">
    <input type="radio" name="carousel" id="slide4">
    <input type="radio" name="carousel" id="slide5">
    <div class="carousel-slide">
        <img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
        <img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
        <img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
        <img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
        <img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
    </div>
    <!-- 左右箭头 -->
    <label for="slide5" class="carousel-btn prev-btn">&#10094;</label>
    <label for="slide2" class="carousel-btn next-btn">&#10095;</label>
    <!-- 底部指示器 -->
    <div class="carousel-indicators">
        <label for="slide1" class="dot active"></label>
        <label for="slide2" class="dot"></label>
        <label for="slide3" class="dot"></label>
        <label for="slide4" class="dot"></label>
        <label for="slide5" class="dot"></label>
    </div>
</div>

这种方法的优势:

  • 无 JavaScript:纯 CSS 实现,代码量少,性能好。
  • 简单直观:状态管理通过 HTML 的 radio 按钮完成,逻辑清晰。

劣势:

  • 浏览器兼容性has() 选择器在旧版浏览器(如 Edge 18-, Firefox 78-, Safari 15.4-)中不被支持。
  • 自动播放实现复杂:实现真正的无限循环自动播放会比较麻烦,通常还是需要一点 JavaScript 来动态切换 radio 按钮的 checked 状态。

使用第三方库 (如 Swiper.js)

在实际项目中,为了快速开发、稳定性和丰富的功能,我们通常会使用成熟的轮播图库。Swiper.js 是目前最受欢迎的选择之一。

优点

  • 功能强大:支持触摸滑动、无限循环、分页、滚动条、延迟加载等几乎所有你能想到的功能。
  • 高度可定制:提供丰富的 API 和配置选项。
  • 性能优异:经过充分优化,在各种设备上都能流畅运行。
  • 文档完善:有非常详细的官方文档和示例。

快速上手 Swiper.js

引入 Swiper 的 CSS 和 JS 文件 你可以从 Swiper 官网 下载文件,或使用 CDN。

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    ...
    <!-- Swiper CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
    <style>
        /* 自定义样式,让 Swiper 容器有固定大小 */
        .swiper {
            width: 800px;
            height: 450px;
        }
        .swiper-slide img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    </style>
</head>
<body>
    <div class="swiper mySwiper">
        <div class="swiper-wrapper">
            <div class="swiper-slide">
                <img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
            </div>
            <div class="swiper-slide">
                <img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
            </div>
            <div class="swiper-slide">
                <img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
            </div>
            <div class="swiper-slide">
                <img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
            </div>
            <div class="swiper-slide">
                <img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
            </div>
        </div>
        <!-- 如果需要分页器 -->
        <div class="swiper-pagination"></div>
        <!-- 如果需要导航按钮 -->
        <div class="swiper-button-next"></div>
        <div class="swiper-button-prev"></div>
        <!-- 如果需要滚动条 -->
        <div class="swiper-scrollbar"></div>
    </div>
    <!-- Swiper JS -->
    <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
    <script>
        // 初始化 Swiper
        const swiper = new Swiper(".mySwiper", {
            // 配置选项
            loop: true, // 循环模式
            pagination: {
                el: ".swiper-pagination",
                clickable: true, // 点击分页器切换
            },
            navigation: {
                nextEl: ".swiper-button-next",
                prevEl: ".swiper-button-prev",
            },
            autoplay: {
                delay: 3000, // 3秒切换一次
                disableOnInteraction: false, // 用户操作后是否停止自动播放
            },
        });
    </script>
</body>
</html>

总结与选择建议

方法 优点 缺点 适用场景
原生 JavaScript - 理解原理,无依赖
- 完全可控,轻量
- 代码量较大,需要处理很多细节
- 实现复杂功能(如无限循环)较繁琐
学习前端、面试、对性能要求极高且不依赖库的项目
CSS has() - 代码极简,性能好
- 纯 CSS 实现
- 浏览器兼容性差
- 实现复杂功能(如自动播放)困难
现代Web项目、追求极致简洁和性能,且兼容性要求不高的场景
Swiper.js 等库 - 功能强大,开箱即用
- 性能优异,稳定可靠
- 文档完善,社区活跃
- 引入外部文件,增加项目体积
- 学习库的 API 和配置
绝大多数实际项目,尤其是商业项目,能极大提高开发效率

对于初学者,强烈建议从方法一(原生 JavaScript)开始,它能让你深刻理解轮播图的实现机制,对于快速开发,直接使用 Swiper.js 是最明智的选择,CSS has() 则是一个展示现代 CSS 强大能力的有趣范例。