JS特效篇:双层轮播图

前言

在上一篇文章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结构说明

上述代码中:

  1. 和普通轮播图相比,双层轮播图只是多了一层内层轮播图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样式说明

上述代码中:

  1. 内层轮播图.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逻辑说明

相比带指示器的轮播图,双层轮播图增加了鼠标交互:

  1. 鼠标进入监听事件:鼠标进入轮播图时,展示内层轮播图,修改外层图片相关属性,并为内层轮播图增加点击事件。
  2. 鼠标离开监听事件:鼠标进入轮播图时,隐藏内层轮播图,修改外层图片相关属性。

结语

本文主要介绍了如何通过CSS+JS实现一个双层轮播图,在后面的文章中,我会继续探讨其他类型的轮播图,你还知道哪些轮播图?欢迎在评论区留言分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值