通过 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 绑定点击事件,直接跳转到对应索引。
各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!