HTML&CSS:有趣的轮播图

通过 Flex + Transform 实现无第三方依赖的极简轮播;底部信息层使用绝对定位 + 伪元素营造"浮起"的圆角效果;再配合响应式断点,兼顾桌面与移动端。JavaScript 仅 20 行左右即可完成自动轮播与手动指示器切换。


大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。

演示效果

HTML&CSS

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .slider {
            position: relative;
            width: 800px;
            max-width: 100%;
            height: 400px;
            margin: auto;
            margin-top: 50px;
            border-radius: 20px;
            overflow: hidden;
        }

        .slides {
            display: flex;
            width: 100%;
            height: 100%;
            transition: transform 0.6s ease-in-out;
            border-radius: 15px 15px 0 0;
        }

        .slide {
            min-width: 100%;
            height: 100%;
            width: 100%;
            object-fit: cover;
            border-radius: 15px 15px 0 0;
            flex-shrink: 0;
            user-select: none;
            pointer-events: none;
        }

        .slide.active {
            pointer-events: auto;
        }

        .bottom-center {
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            width: 60%;
            background: rgba(0, 0, 0, 0.3);
            padding: 15px 20px;
            padding-bottom: 0;
            text-align: center;
            box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
            z-index: 2;
            overflow: visible;
            border-radius: 20px 20px 0 0;
            transition: all 0.3s ease-in-out;
        }

        /* Outer radius using pseudo-elements */
        .bottom-center::before,
        .bottom-center::after {
            content: "";
            position: absolute;
            bottom: 0;
            width: 50px;
            height: 40px;
            background: rgba(0, 0, 0, 0.3);
            border-radius: 0;
            z-index: -1;
        }

        .bottom-center::before {
            left: -30px;
            bottom: -10px;
            mask: radial-gradient(30px at top left, #0000 98%, #000);
        }

        .bottom-center::after {
            right: -30px;
            bottom: -10px;
            mask: radial-gradient(30px at top right, #0000 98%, #000);
        }

        .bottom-center h3 {
            margin: 5px 0;
            font-size: 24px;
            color: #fff;
        }

        .bottom-center p {
            margin: 5px 0 10px;
            color: #fff;
            font-size: 18px;
        }

        .luxury-heading {
            font-family: "Gloock", serif;
            font-weight: 700;
            font-size: 2.9rem;
            color: #fff;
            letter-spacing: 0.05em;
            margin-bottom: 0.3em;
            text-transform: capitalize;
            text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
        }

        .luxury-subtitle {
            font-family: "Montserrat", sans-serif;
            font-weight: 400;
            font-size: 1.2rem;
            color: #fff;
            letter-spacing: 0.04em;
            line-height: 1.5;
            margin-top: 0;
            opacity: 0.85;
        }

        .buttons {
            margin-top: 20px;
            margin-bottom: 12px;
            text-align: center;
        }

        .btn {
            font-family: "Segoe UI", "Helvetica Neue", sans-serif;
            font-weight: 600;
            font-size: 1rem;
            padding: 0.75em 1.5em;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            letter-spacing: 0.5px;
            margin: 0 10px;
            user-select: none;
            margin-bottom: 10px;
        }

        .btn.primary {
            background-color: #ffc727;
            color: #222;
            box-shadow: 0 4px 12px rgba(255, 199, 39, 0.4);
        }

        .btn.primary:hover {
            background-color: #ffb400;
            box-shadow: 0 6px 16px rgba(255, 180, 0, 0.5);
            transform: translateY(-2px);
        }

        .btn.secondary {
            background-color: transparent;
            color: #ffc727;
            border: 2px solid #ffc727;
        }

        .btn.secondary:hover {
            background-color: #fff8e1;
            color: #222;
            border-color: #ffb400;
            transform: translateY(-2px);
        }

        .indicators {
            display: flex;
            justify-content: center;
            gap: 8px;
            margin-top: 20px;
        }

        .dot {
            height: 10px;
            width: 10px;
            background-color: #ccc;
            border-radius: 50%;
            display: inline-block;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        .dot.active {
            background-color: #333;
        }


        @media(max-width:768px) {
            .slider {
                width: 95%;
                height: 400px;
            }

            .slider {
                border-radius: 10px;
            }

            .btn {
                margin: 5px;
            }

            .bottom-center {
                width: 80%;
                border-radius: 10px;
            }

            .bottom-center::before {
                left: -20px;
                bottom: -20px;
                mask: radial-gradient(20px at top left, #0000 98%, #000);
            }

            .bottom-center::after {
                right: -20px;
                bottom: -20px;
                mask: radial-gradient(20px at top right, #0000 98%, #000);
            }
        }

        @media(max-width:414px) {
            .bottom-center {
                border-radius: 10px 10px 0 0;
            }

            .btn {
                width: 100%;
                margin: 5px 0;
            }

            .bottom-center::before,
            .bottom-center::after {
                display: none;
            }

        }
    </style>
</head>

<body>
    <div class="slider">
        <div class="slides">
            <img src="https://mmbiz.qpic.cn/mmbiz_jpg/XsgV6wHhAdF5JxOMbL2Kud3zVdKZ2Fr5zNZ4JAb7Y2vu29qtG0BlkN2AXUJNt1BDh50T8oMbBptZyKP3KErtTA/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1&randomid=i099zcd3&tp=wxpic"
                class="slide active" />
            <img src="https://mmbiz.qpic.cn/mmbiz_jpg/XsgV6wHhAdHOuz3dhrwRbnYpOf2cYrMBGYo3jp8ViaOQSp97yALHgzED76JWKesZ8M4aLD7otrHFTiamtzdicFEwA/640?wx_fmt=jpeg&from=appmsg&randomid=1n8k9nz4&tp=wxpic&wxfrom=5&wx_lazy=1"
                class="slide" />
            <img src="https://mmbiz.qpic.cn/mmbiz_jpg/XsgV6wHhAdHOuz3dhrwRbnYpOf2cYrMBsdaaQqCGBNFdwxIDFxnxS2Hjicj9hmfBkgmMibqzWCFWzgKWZwl5AKiaA/640?wx_fmt=jpeg&from=appmsg&randomid=ymlhj2qc&tp=wxpic&wxfrom=5&wx_lazy=1"
                class="slide">
        </div>

        <div class="bottom-center">
            <h3 class="luxury-heading">NBA电脑壁纸 | 高清横屏球星壁纸</h3>
            <p class="luxury-subtitle">你最喜欢哪一张?</p>

            <div class="buttons">
                <button class="btn primary">下载</button>
                <button class="btn secondary">更多</button>
            </div>

            <div class="indicators">
                <span class="dot active" onclick="showSlide(0)"></span>
                <span class="dot" onclick="showSlide(1)"></span>
                <span class="dot" onclick="showSlide(2)"></span>
            </div>
        </div>
    </div>
    <script>
        let currentSlide = 0;
        const slides = document.querySelectorAll(".slide");
        const dots = document.querySelectorAll(".dot");
        const slidesContainer = document.querySelector(".slides");

        function showSlide(index) {
            if (index < 0) index = slides.length - 1;
            if (index >= slides.length) index = 0;
            currentSlide = index;
            slidesContainer.style.transform = `translateX(-${currentSlide * 100}%)`;
            dots.forEach((dot) => dot.classList.remove("active"));
            dots[currentSlide].classList.add("active");
        }

        setInterval(() => {
            showSlide(currentSlide + 1);
        }, 1500);

        dots.forEach((dot, idx) => {
            dot.addEventListener("click", () => showSlide(idx));
        });

    </script>
</body>

</html>

HTML

  • about-title:页面主标题
  • carousel-container:轮播整体容器
  • nav-arrow left / right:左右箭头按钮
  • carousel-track:轮播轨道
  • card:单个成员卡片
  • member-info:当前选中成员信息
  • member-name:成员姓名
  • dots:指示点容器
  • dot:单个指示点

CSS

  • *:把所有元素的默认外边距、内边距清零,统一使用 border-box 计算宽高,避免 padding 撑破布局;
  • body:让整个页面在视口中水平、垂直都居中;min-height:100vh 保证即使内容少也占满一屏;
  • .about-title:3 rem 大字号先抓眼球;用线性渐变当文字填充色,再把文字本身设为透明
  • .carousel-container:相对定位给内部绝对定位的箭头当参照;perspective 让卡片在 Z 轴移动时真正有"近大远小"的立体效果;超出区域隐藏,避免滚动条。
  • .carousel-track:用 Flex 把卡片横向排成一排;align-items:center 让卡片在容器里垂直居中;transform 过渡 0.6 s,做平滑的左右滑动动画。
  • .card:卡片默认缩小到 0.85,营造「中间突出、两边退后」的层次感;圆角 + 阴影让卡片像浮起来;所有属性都加过渡,切换状态时有动画。
  • .card.center:选中卡片放大回 1 倍,再沿 Z 轴向前推 60 px,立体感更强;
  • .card.left-1 / .right-1:左右两侧的卡片再往侧边移 150 px,缩到 0.75,透明度降到 0.6,制造"退到后面"的视觉效果。
  • .member-info:成员姓名与职位放在轮播下方居中;切换成员时利用 0.4 s 的淡入淡出过渡,避免文字突变。
  • .member-name:2 rem 粗黑体,确保姓名醒目;深灰比纯黑柔和。
  • .member-role:稍小、稍淡的字体,与姓名形成层级,不会喧宾夺主。
  • .dots:指示点用 Flex 居中排布,gap:10px 等距;顶部留 20 px 把圆点和轮播主体隔开。
  • .dot:未选中时浅灰小圆点,鼠标呈手型,暗示可点击。
  • .dot.active:当前页面对应的圆点变成黑色并放大 1.3 倍,一眼就能看到处于哪一页。
  • .nav-arrow:箭头按钮绝对定位到容器垂直中心;半透明黑底白字,圆形成"悬浮按钮";过渡让背景色变化顺滑。
  • .nav-arrow:hover:鼠标悬停背景加深,提升可点击反馈。

JavaScript

JavaScript 复制代码
let currentSlide = 0;
const slides = document.querySelectorAll('.slide');
const dots   = document.querySelectorAll('.dot');
const slidesContainer = document.querySelector('.slides');

/* 切换到指定索引 */
function showSlide(index){
  if (index < 0) index = slides.length - 1;
  if (index >= slides.length) index = 0;
  currentSlide = index;

  slidesContainer.style.transform = `translateX(-${currentSlide * 100}%)`;
  dots.forEach(d => d.classList.remove('active'));
  dots[currentSlide].classList.add('active');
}

/* 自动轮播 */
setInterval(() => {
  showSlide(currentSlide + 1);
}, 1500);

/* 点击小圆点手动切换 */
dots.forEach((dot, idx) => {
  dot.addEventListener('click', () => showSlide(idx));
});
  • 用 transform: translateX(-n * 100%) 让轨道整体左移,n 为当前索引。
  • 自动播放:setInterval 每 1.5 s 递增索引并调用 showSlide。
  • 手动控制:给每个 .dot 绑定点击事件,直接跳转到对应索引。

各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!

相关推荐
让辣条自由翱翔3 分钟前
总结一下Vue的组件通信
前端
dyb4 分钟前
开箱即用的Next.js SSR企业级开发模板
前端·react.js·next.js
前端的日常5 分钟前
Vite 如何处理静态资源?
前端
前端的日常5 分钟前
如何在 Vite 中配置路由?
前端
兮漫天6 分钟前
bun + vite7 的结合,孕育的 Robot Admin 靓仔出道(一)
前端
PineappleCoder7 分钟前
JS 作用域链拆解:变量查找的 “俄罗斯套娃” 规则
前端·javascript·面试
兮漫天7 分钟前
bun + vite7 的结合,孕育的 Robot Admin 靓仔出道(二)
前端
用户479492835691512 分钟前
面试官:为什么很多格式化工具都会在行尾额外空出一行
前端
知识分享小能手12 分钟前
Vue3 学习教程,从入门到精通,Vue3 中使用 Axios 进行 Ajax 请求的语法知识点与案例代码(23)
前端·javascript·vue.js·学习·ajax·vue·vue3
一大树13 分钟前
首屏白屏的处理方案~嗖得一下
前端