#

图片轮播组件开发

By DoubleSpirit121 1 Views 18 MIN READ 0 Comments
详细介绍如何开发一个高性能的图片轮播组件ImageGallery,用于在VitePress中展示图片集。涵盖组件需求分析、Vue3+TypeScript代码实现、组件注册方法、响应式布局与暗色模式适配、箭头切换与缩略图导航等核心功能的完整开发流程。

概述

本文介绍如何开发一个高性能的图片轮播组件 ImageGallery,用于在 VitePress 中展示图片集。

组件需求

  1. 大图展示(圆角矩形,与 BiliVideo 风格一致)
  2. 底部缩略图列表
  3. 鼠标悬停大图显示左右箭头切换
  4. 图片自动缩放填满框
  5. 支持本地图片和图链
  6. 轻量快速,不影响加载速度

组件代码

创建文件 docs/.vitepress/theme/components/ImageGallery.vue

<script setup lang="ts">
import { ref } from 'vue'

interface Props {
  images: {
    src: string
    alt?: string
  }[]
}

const props = defineProps<Props>()

const activeIndex = ref(0)
const showArrows = ref(false)

const prev = () => {
  activeIndex.value = activeIndex.value === 0 ? props.images.length - 1 : activeIndex.value - 1
}

const next = () => {
  activeIndex.value = activeIndex.value === props.images.length - 1 ? 0 : activeIndex.value + 1
}
</script>

<template>
  <div v-if="images && images.length > 0" class="image-gallery">
    <div 
      class="main-image"
      @mouseenter="showArrows = true"
      @mouseleave="showArrows = false"
    >
      <img 
        :src="images[activeIndex].src" 
        :alt="images[activeIndex].alt || 'gallery image'"
      />
      
      <div v-if="images.length > 1" class="arrow arrow-left" :class="{ visible: showArrows }" @click="prev">
        <svg viewBox="0 0 24 24" width="24" height="24">
          <path fill="currentColor" d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
        </svg>
      </div>
      
      <div v-if="images.length > 1" class="arrow arrow-right" :class="{ visible: showArrows }" @click="next">
        <svg viewBox="0 0 24 24" width="24" height="24">
          <path fill="currentColor" d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
        </svg>
      </div>
    </div>
    
    <div class="thumbnails">
      <div
        v-for="(img, index) in images" 
        :key="index"
        class="thumbnail"
        :class="{ active: index === activeIndex }"
      >
        <img :src="img.src" :alt="img.alt || `thumbnail ${index}`" />
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.image-gallery {
  width: 100%;
  margin: 1.5rem 0;
}

.main-image {
  width: 100%;
  position: relative;
  padding-bottom: 56.25%;
  border-radius: 12px;
  overflow: hidden;
  background: var(--vp-c-bg-soft);
  box-shadow: 0 0 20px rgba(255, 255, 255, 0);

  @media (prefers-color-scheme: dark) {
    box-shadow: 0 0 20px rgba(255, 255, 255, 0.1), 0 0 10px rgba(255, 255, 255, 0.2);
  }

  img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

.arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  color: white;
  border-radius: 50%;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.3s ease;
  
  &.visible {
    opacity: 1;
  }
  
  &:hover {
    background: rgba(0, 0, 0, 0.7);
  }
  
  svg {
    width: 24px;
    height: 24px;
  }
}

.arrow-left {
  left: 10px;
}

.arrow-right {
  right: 10px;
}

.thumbnails {
  display: flex;
  gap: 10px;
  margin-top: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

.thumbnail {
  width: 80px;
  height: 60px;
  border-radius: 8px;
  overflow: hidden;
  opacity: 0.5;
  transition: all 0.3s ease;
  border: 2px solid transparent;
  cursor: pointer;
  
  &:hover {
    opacity: 0.8;
  }
  
  &.active {
    opacity: 1;
    border-color: var(--vp-c-brand);
  }
  
  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

@media (max-width: 640px) {
  .thumbnail {
    width: 60px;
    height: 45px;
  }
  
  .arrow {
    width: 32px;
    height: 32px;
    
    svg {
      width: 20px;
      height: 20px;
    }
  }
}
</style>

注册组件

docs/.vitepress/theme/index.ts 中注册组件:

import ImageGallery from "./components/ImageGallery.vue";

export default {
  enhanceApp({ app }: { app: any }) {
    app.component('ImageGallery', ImageGallery);
  },
};

使用方法

在 Markdown 文件中使用:

<ImageGallery :images="[
  { src: '图片1地址.jpg', alt: '图片1' },
  { src: '图片2地址.jpg', alt: '图片2' },
  { src: '图片3地址.jpg', alt: '图片3' }
]" />

关键实现点

1. 类型定义

使用 TypeScript 接口定义图片数组结构:

interface Props {
  images: {
    src: string
    alt?: string
  }[]
}

2. 图片填充

使用 padding-bottom 百分比实现响应式 16:9 容器:

.main-image {
  padding-bottom: 56.25%; // 16:9 比例
}

img {
  object-fit: cover; // 图片填满容器
}

3. 箭头切换

使用 v-if 控制箭头显示,通过点击事件切换图片:

const showArrows = ref(false)

const prev = () => {
  activeIndex.value = activeIndex.value === 0 ? props.images.length - 1 : activeIndex.value - 1
}

4. 暗色模式适配

使用 CSS 变量和媒体查询适配暗色模式:

@media (prefers-color-scheme: dark) {
  box-shadow: 0 0 20px rgba(255, 255, 255, 0.1), 0 0 10px rgba(255, 255, 255, 0.2);
}

本文由 DoubleSpirit121 原创

采用 CC BY-NC-SA 4.0 协议进行许可

转载请注明出处:https://blog.mcoo.top/index.php/archives/39/

加入 Mcoo

0 评论

发表评论