前言
在上一篇文章JS特效篇:指示器轮播图中,我们讲述了如何使用CSS+JS实现一个带指示器的轮播图。本文将讲述如何通过CSS+JS,实现一个双层轮播图,并详细讲解其中的原理和关键代码。
效果预览
双层轮播图其实是对指示器轮播图的一种改进,当轮播图片过多时,我们想更加清楚地选择要切换的图片,这时候普通指示器就无法满足我们的需求了,就可以考虑使用双层轮播图,其效果如下所示:
代码实现
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>双层轮播图</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
max-width: 60vw;
width: 100%;
margin: 0 auto;
}
/* 轮播图样式 */
.carousel-container {
position: relative;
width: 100%;
height: 500px;
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.carousel-outer {
position: absolute;
inset: 0;
transition: opacity 0.3s ease;
}
.carousel-outer-items {
height: 100%;
width: 100%;
position: relative;
}
.carousel-item {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: opacity 0.7s ease-in-out;
}
.carousel-item .gradient-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
display: flex;
align-items: flex-end;
}
.carousel-item .content {
padding: 2rem;
color: white;
}
.carousel-item .content h2 {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.carousel-item .content p {
font-size: 1.125rem;
max-width: 40rem;
}
/* 内层轮播图 */
.carousel-inner {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.carousel-inner-content {
width: 100%;
max-width: 64rem;
margin: 0 auto;
}
.inner-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
padding: 1rem;
}
.carousel-inner-item {
cursor: pointer;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
transform: translateZ(0);
transition: transform 0.3s ease;
}
.carousel-inner-item:hover {
transform: scale(1.05);
}
.inner-image {
height: 12rem;
background-size: cover;
background-position: center;
}
.inner-content {
background-color: white;
padding: 0.75rem;
}
.inner-content h3 {
font-weight: 600;
}
/* 响应式调整 */
@media (max-width: 768px) {
.carousel-container {
height: 350px;
}
.inner-grid {
grid-template-columns: repeat(1, 1fr);
}
.carousel-item .content h2 {
font-size: 1.5rem;
}
.carousel-item .content p {
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="container">
<!-- 轮播图容器 -->
<div class="carousel-container">
<!-- 外层轮播图 -->
<div class="carousel-outer">
<div class="carousel-outer-items">
<!-- 外层轮播图项目 -->
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/29/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>雪山耸立</h2>
<p>宁静的雪山顶漂浮白云朵朵</p>
</div>
</div>
</div>
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/10/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>林中小湖</h2>
<p>山林之间的平静小湖</p>
</div>
</div>
</div>
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/28/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>山中峡谷</h2>
<p>探索壮丽的自然奇观</p>
</div>
</div>
</div>
</div>
</div>
<!-- 内层轮播图 -->
<div class="carousel-inner">
<div class="carousel-inner-content">
<div class="inner-grid">
<!-- 内层轮播图项目 -->
<div class="carousel-inner-item" data-index="0">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/29/750/400');"></div>
<div class="inner-content">
<h3>雪山耸立</h3>
</div>
</div>
<div class="carousel-inner-item" data-index="1">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/10/750/400');"></div>
<div class="inner-content">
<h3>林中小湖</h3>
</div>
</div>
<div class="carousel-inner-item" data-index="2">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/28/750/400');"></div>
<div class="inner-content">
<h3>山中峡谷</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 轮播图功能实现
document.addEventListener('DOMContentLoaded', function() {
const outerItems = document.querySelectorAll('.carousel-outer .carousel-item');
const innerItems = document.querySelectorAll('.carousel-inner-item');
let currentIndex = 0;
const carouselContainer = document.querySelector('.carousel-container');
const carouselOuter = document.querySelector('.carousel-outer');
const carouselInner = document.querySelector('.carousel-inner');
// 设置外层轮播图初始状态
function setInitialState() {
outerItems.forEach((item, index) => {
item.style.opacity = index === currentIndex ? '1' : '0';
});
}
// 切换到指定索引的图片
function switchToSlide(index) {
// 淡出当前图片
outerItems[currentIndex].style.opacity = '0';
// 更新当前索引
currentIndex = index;
// 淡入新图片
outerItems[currentIndex].style.opacity = '1';
}
// 为内层轮播图项目添加点击事件
innerItems.forEach((item, index) => {
item.addEventListener('click', () => {
switchToSlide(index);
});
});
// 设置初始状态
setInitialState();
// 添加自动轮播功能
let interval = setInterval(() => {
let nextIndex = (currentIndex + 1) % outerItems.length;
switchToSlide(nextIndex);
}, 1000);
// 鼠标交互
carouselContainer.addEventListener('mouseenter', () => {
clearInterval(interval);
carouselInner.style.opacity = '1';
carouselInner.style.visibility = 'visible';
carouselOuter.style.opacity = '0.4';
});
carouselContainer.addEventListener('mouseleave', () => {
interval = setInterval(() => {
let nextIndex = (currentIndex + 1) % outerItems.length;
switchToSlide(nextIndex);
}, 1000);
carouselInner.style.opacity = '0';
carouselInner.style.visibility = 'hidden';
carouselOuter.style.opacity = '1';
});
});
</script>
</body>
</html>
代码说明
HTML结构
<div class="container">
<!-- 轮播图容器 -->
<div class="carousel-container">
<!-- 外层轮播图 -->
<div class="carousel-outer">
<div class="carousel-outer-items">
<!-- 外层轮播图项目 -->
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/29/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>雪山耸立</h2>
<p>宁静的雪山顶漂浮白云朵朵</p>
</div>
</div>
</div>
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/10/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>林中小湖</h2>
<p>山林之间的平静小湖</p>
</div>
</div>
</div>
<div class="carousel-item" style="background-image: url('https://picsum.photos/id/28/750/400');">
<div class="gradient-overlay">
<div class="content">
<h2>山中峡谷</h2>
<p>探索壮丽的自然奇观</p>
</div>
</div>
</div>
</div>
</div>
<!-- 内层轮播图 -->
<div class="carousel-inner">
<div class="carousel-inner-content">
<div class="inner-grid">
<!-- 内层轮播图项目 -->
<div class="carousel-inner-item" data-index="0">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/29/750/400');"></div>
<div class="inner-content">
<h3>雪山耸立</h3>
</div>
</div>
<div class="carousel-inner-item" data-index="1">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/10/750/400');"></div>
<div class="inner-content">
<h3>林中小湖</h3>
</div>
</div>
<div class="carousel-inner-item" data-index="2">
<div class="inner-image" style="background-image: url('https://picsum.photos/id/28/750/400');"></div>
<div class="inner-content">
<h3>山中峡谷</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
HTML结构说明
上述代码中:
- 和普通轮播图相比,双层轮播图只是多了一层内层轮播图
carousel-inner
。
CSS样式
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
max-width: 60vw;
width: 100%;
margin: 0 auto;
}
/* 轮播图样式 */
.carousel-container {
position: relative;
width: 100%;
height: 500px;
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.carousel-outer {
position: absolute;
inset: 0;
transition: opacity 0.3s ease;
}
.carousel-outer-items {
height: 100%;
width: 100%;
position: relative;
}
.carousel-item {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: opacity 0.7s ease-in-out;
}
.carousel-item .gradient-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
display: flex;
align-items: flex-end;
}
.carousel-item .content {
padding: 2rem;
color: white;
}
.carousel-item .content h2 {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.carousel-item .content p {
font-size: 1.125rem;
max-width: 40rem;
}
/* 内层轮播图 */
.carousel-inner {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.carousel-inner-content {
width: 100%;
max-width: 64rem;
margin: 0 auto;
}
.inner-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
padding: 1rem;
}
.carousel-inner-item {
cursor: pointer;
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
transform: translateZ(0);
transition: transform 0.3s ease;
}
.carousel-inner-item:hover {
transform: scale(1.05);
}
.inner-image {
height: 12rem;
background-size: cover;
background-position: center;
}
.inner-content {
background-color: white;
padding: 0.75rem;
}
.inner-content h3 {
font-weight: 600;
}
/* 响应式调整 */
@media (max-width: 768px) {
.carousel-container {
height: 350px;
}
.inner-grid {
grid-template-columns: repeat(1, 1fr);
}
.carousel-item .content h2 {
font-size: 1.5rem;
}
.carousel-item .content p {
font-size: 1rem;
}
}
</style>
CSS样式说明
上述代码中:
- 内层轮播图:
.carousel-inner
定义了一个内层轮播图容器,实现图片在容器中平铺展示。
JS逻辑
<script>
// 轮播图功能实现
document.addEventListener('DOMContentLoaded', function() {
const outerItems = document.querySelectorAll('.carousel-outer .carousel-item');
const innerItems = document.querySelectorAll('.carousel-inner-item');
let currentIndex = 0;
const carouselContainer = document.querySelector('.carousel-container');
const carouselOuter = document.querySelector('.carousel-outer');
const carouselInner = document.querySelector('.carousel-inner');
// 设置外层轮播图初始状态
function setInitialState() {
outerItems.forEach((item, index) => {
item.style.opacity = index === currentIndex ? '1' : '0';
});
}
// 切换到指定索引的图片
function switchToSlide(index) {
// 淡出当前图片
outerItems[currentIndex].style.opacity = '0';
// 更新当前索引
currentIndex = index;
// 淡入新图片
outerItems[currentIndex].style.opacity = '1';
}
// 为内层轮播图项目添加点击事件
innerItems.forEach((item, index) => {
item.addEventListener('click', () => {
switchToSlide(index);
});
});
// 设置初始状态
setInitialState();
// 添加自动轮播功能
let interval = setInterval(() => {
let nextIndex = (currentIndex + 1) % outerItems.length;
switchToSlide(nextIndex);
}, 1000);
// 鼠标交互
carouselContainer.addEventListener('mouseenter', () => {
clearInterval(interval);
carouselInner.style.opacity = '1';
carouselInner.style.visibility = 'visible';
carouselOuter.style.opacity = '0.4';
});
carouselContainer.addEventListener('mouseleave', () => {
interval = setInterval(() => {
let nextIndex = (currentIndex + 1) % outerItems.length;
switchToSlide(nextIndex);
}, 1000);
carouselInner.style.opacity = '0';
carouselInner.style.visibility = 'hidden';
carouselOuter.style.opacity = '1';
});
});
</script>
JS逻辑说明
相比带指示器的轮播图,双层轮播图增加了鼠标交互:
- 鼠标进入监听事件:鼠标进入轮播图时,展示内层轮播图,修改外层图片相关属性,并为内层轮播图增加点击事件。
- 鼠标离开监听事件:鼠标进入轮播图时,隐藏内层轮播图,修改外层图片相关属性。
结语
本文主要介绍了如何通过CSS+JS实现一个双层轮播图,在后面的文章中,我会继续探讨其他类型的轮播图,你还知道哪些轮播图?欢迎在评论区留言分享!