Carousel 走马灯 
概述 
Carousel 走马灯组件用于在有限空间内,循环播放同一类型的图片、文字等内容。常用于轮播图、产品展示、广告位等场景,提供了自动播放、手动切换、指示器等功能。
学习目标 
- 掌握 Carousel 组件的基础用法
- 学会配置自动播放和切换效果
- 理解指示器和切换箭头的使用
- 掌握垂直方向和卡片化的走马灯
- 了解 Carousel 组件的 API 和最佳实践
基础用法 
基础走马灯 
适用广泛的基础用法。结合使用 el-carousel 和 el-carousel-item 标签就得到了一个走马灯。幻灯片的内容是任意的,需要放在 el-carousel-item 标签中。默认情况下,在鼠标 hover 底部的指示器时就会触发切换。通过设置 trigger 属性为 click,可以达到点击触发的效果。
<template>
  <div class="carousel-demo">
    <h3>基础走马灯</h3>
    <el-carousel height="150px">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3 class="small" justify="center">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<style scoped>
.el-carousel__item h3 {
  color: #475669;
  opacity: 0.75;
  line-height: 150px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>指示器 
可以将指示器的显示位置设置在容器外部。indicator-position 属性定义了指示器的位置。默认情况下,它会显示在走马灯内部,设置为 outside 则会显示在外部;设置为 none 则不会显示指示器。
<template>
  <div class="indicator-demo">
    <h3>外部指示器</h3>
    <el-carousel indicator-position="outside">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3 class="medium" justify="center">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<style scoped>
.el-carousel__item h3 {
  color: #475669;
  opacity: 0.75;
  line-height: 200px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>切换箭头 
可以设置切换箭头的显示时机。arrow 属性定义了切换箭头的显示时机。默认情况下,切换箭头只有在鼠标 hover 到走马灯上时才会显示;若将 arrow 设置为 always,则会一直显示;设置为 never,则会一直隐藏。
<template>
  <div class="arrow-demo">
    <h3>切换箭头</h3>
    <el-carousel :interval="4000" arrow="always">
      <el-carousel-item v-for="item in 4" :key="item">
        <h3 class="medium" justify="center">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<style scoped>
.el-carousel__item h3 {
  color: #475669;
  opacity: 0.75;
  line-height: 200px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>卡片化 
当页面宽度方向空间空余,但高度方向空间匮乏时,可使用卡片风格。将 type 设置为 card 即可启用卡片模式。从性能的角度考虑,当切换到卡片模式时,只有两侧的卡片会渲染。
<template>
  <div class="card-demo">
    <h3>卡片化</h3>
    <el-carousel :interval="4000" type="card" height="200px">
      <el-carousel-item v-for="item in 6" :key="item">
        <h3 class="medium" justify="center">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<style scoped>
.el-carousel__item h3 {
  color: #475669;
  opacity: 0.75;
  line-height: 200px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>方向 
默认情况下,direction 为 horizontal。通过设置 direction 为 vertical 来让走马灯在垂直方向上显示。
<template>
  <div class="vertical-demo">
    <h3>垂直方向</h3>
    <el-carousel height="200px" direction="vertical" :autoplay="false">
      <el-carousel-item v-for="item in 3" :key="item">
        <h3 class="medium" justify="center">{{ item }}</h3>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<style scoped>
.el-carousel__item h3 {
  color: #475669;
  opacity: 0.75;
  line-height: 200px;
  margin: 0;
  text-align: center;
}
.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
  background-color: #d3dce6;
}
</style>实际应用示例 
图片轮播 
<template>
  <div class="image-carousel-demo">
    <h3>图片轮播</h3>
    <el-carousel 
      :interval="5000" 
      arrow="always" 
      indicator-position="outside"
      height="300px"
    >
      <el-carousel-item v-for="(image, index) in images" :key="index">
        <div class="carousel-image">
          <img :src="image.url" :alt="image.title" />
          <div class="image-overlay">
            <h4>{{ image.title }}</h4>
            <p>{{ image.description }}</p>
          </div>
        </div>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const images = ref([
  {
    url: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    title: '美味汉堡',
    description: '新鲜制作,口感丰富'
  },
  {
    url: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    title: '精选食材',
    description: '优质原料,健康美味'
  },
  {
    url: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    title: '特色套餐',
    description: '搭配饮品,超值享受'
  }
])
</script>
<style scoped>
.carousel-image {
  position: relative;
  width: 100%;
  height: 100%;
}
.carousel-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.image-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
  color: white;
  padding: 20px;
}
.image-overlay h4 {
  margin: 0 0 8px 0;
  font-size: 18px;
}
.image-overlay p {
  margin: 0;
  font-size: 14px;
  opacity: 0.9;
}
</style>产品展示轮播 
<template>
  <div class="product-carousel-demo">
    <h3>产品展示轮播</h3>
    <el-carousel 
      type="card" 
      height="250px" 
      :autoplay="false"
      @change="handleCarouselChange"
    >
      <el-carousel-item v-for="product in products" :key="product.id">
        <div class="product-card">
          <div class="product-image">
            <img :src="product.image" :alt="product.name" />
          </div>
          <div class="product-info">
            <h4>{{ product.name }}</h4>
            <p class="price">¥{{ product.price }}</p>
            <el-rate v-model="product.rating" disabled size="small" />
          </div>
        </div>
      </el-carousel-item>
    </el-carousel>
    
    <div class="current-product" v-if="currentProduct">
      <h4>当前展示:{{ currentProduct.name }}</h4>
      <p>{{ currentProduct.description }}</p>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const products = ref([
  {
    id: 1,
    name: 'MacBook Pro',
    price: 12999,
    rating: 5,
    image: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    description: '强大的专业级笔记本电脑,适合开发和设计工作'
  },
  {
    id: 2,
    name: 'iPhone 15 Pro',
    price: 8999,
    rating: 5,
    image: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    description: '最新的智能手机,拍照和性能都很出色'
  },
  {
    id: 3,
    name: 'iPad Air',
    price: 4599,
    rating: 4,
    image: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    description: '轻薄便携的平板电脑,适合娱乐和轻办公'
  },
  {
    id: 4,
    name: 'Apple Watch',
    price: 2999,
    rating: 4,
    image: 'https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png',
    description: '智能手表,健康监测和运动追踪功能强大'
  }
])
const currentProduct = ref(null)
const handleCarouselChange = (index) => {
  currentProduct.value = products.value[index]
}
onMounted(() => {
  currentProduct.value = products.value[0]
})
</script>
<style scoped>
.product-card {
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  height: 100%;
  display: flex;
  flex-direction: column;
}
.product-image {
  height: 150px;
  overflow: hidden;
}
.product-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.product-info {
  padding: 16px;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.product-info h4 {
  margin: 0 0 8px 0;
  font-size: 16px;
  font-weight: 600;
}
.price {
  margin: 8px 0;
  font-size: 18px;
  font-weight: bold;
  color: #f56c6c;
}
.current-product {
  margin-top: 20px;
  padding: 16px;
  background: #f5f7fa;
  border-radius: 8px;
}
.current-product h4 {
  margin: 0 0 8px 0;
  color: #409eff;
}
.current-product p {
  margin: 0;
  color: #666;
}
</style>新闻公告轮播 
<template>
  <div class="news-carousel-demo">
    <h3>新闻公告轮播</h3>
    <el-carousel 
      direction="vertical" 
      height="120px" 
      :autoplay="true"
      :interval="3000"
      indicator-position="none"
      arrow="never"
    >
      <el-carousel-item v-for="news in newsList" :key="news.id">
        <div class="news-item" @click="handleNewsClick(news)">
          <div class="news-content">
            <h4>{{ news.title }}</h4>
            <p>{{ news.summary }}</p>
            <span class="news-date">{{ news.date }}</span>
          </div>
          <div class="news-type">
            <el-tag :type="news.type === 'important' ? 'danger' : 'info'" size="small">
              {{ news.type === 'important' ? '重要' : '通知' }}
            </el-tag>
          </div>
        </div>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
const newsList = ref([
  {
    id: 1,
    title: '系统维护通知',
    summary: '系统将于今晚22:00-24:00进行维护升级',
    date: '2024-01-15',
    type: 'important'
  },
  {
    id: 2,
    title: '新功能上线',
    summary: '数据导出功能已上线,支持多种格式导出',
    date: '2024-01-14',
    type: 'normal'
  },
  {
    id: 3,
    title: '安全提醒',
    summary: '请定期更新密码,确保账户安全',
    date: '2024-01-13',
    type: 'important'
  },
  {
    id: 4,
    title: '使用指南',
    summary: '新用户使用指南已更新,欢迎查看',
    date: '2024-01-12',
    type: 'normal'
  }
])
const handleNewsClick = (news) => {
  ElMessage.info(`点击了新闻:${news.title}`)
}
</script>
<style scoped>
.news-item {
  display: flex;
  align-items: center;
  padding: 16px;
  background: white;
  border-radius: 8px;
  margin: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  cursor: pointer;
  transition: all 0.3s;
}
.news-item:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transform: translateY(-2px);
}
.news-content {
  flex: 1;
}
.news-content h4 {
  margin: 0 0 4px 0;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}
.news-content p {
  margin: 0 0 8px 0;
  font-size: 14px;
  color: #606266;
  line-height: 1.4;
}
.news-date {
  font-size: 12px;
  color: #909399;
}
.news-type {
  margin-left: 16px;
}
</style>API 文档 
Carousel Attributes 
| 属性名 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| height | 走马灯的高度 | string | — | 
| initial-index | 初始状态激活的幻灯片的索引,从 0 开始 | number | 0 | 
| trigger | 指示器的触发方式 | enum: hover/click | hover | 
| autoplay | 是否自动切换 | boolean | true | 
| interval | 自动切换的时间间隔,单位为毫秒 | number | 3000 | 
| indicator-position | 指示器的位置 | enum: none/outside | — | 
| arrow | 切换箭头的显示时机 | enum: always/hover/never | hover | 
| type | 走马灯的类型 | enum: card | — | 
| loop | 是否循环显示 | boolean | true | 
| direction | 走马灯展示的方向 | enum: horizontal/vertical | horizontal | 
| pause-on-hover | 鼠标悬浮时暂停自动切换 | boolean | true | 
Carousel Events 
| 事件名 | 说明 | 回调参数 | 
|---|---|---|
| change | 幻灯片切换时触发 | 目前激活的幻灯片的索引,原幻灯片的索引 | 
Carousel Methods 
| 方法名 | 说明 | 参数 | 
|---|---|---|
| setActiveItem | 手动切换幻灯片 | 需要切换的幻灯片的索引,从 0 开始;或相应 el-carousel-item的name属性值 | 
| prev | 切换至上一张幻灯片 | — | 
| next | 切换至下一张幻灯片 | — | 
Carousel-Item Attributes 
| 属性名 | 说明 | 类型 | 默认值 | 
|---|---|---|---|
| name | 幻灯片的名字,可用作 setActiveItem的参数 | string | — | 
| label | 该幻灯片所对应指示器的文本 | string | — | 
最佳实践 
性能优化 
- 图片懒加载:对于图片轮播,使用懒加载减少初始加载时间
- 合理的切换间隔:根据内容类型设置合适的自动切换时间
- 卡片模式优化:卡片模式下只渲染必要的幻灯片
用户体验 
- 暂停控制:鼠标悬浮时暂停自动播放
- 指示器设计:提供清晰的当前位置指示
- 触摸支持:在移动设备上支持滑动切换
可访问性 
- 键盘导航:支持键盘方向键切换
- 屏幕阅读器:为图片提供 alt 属性
- 焦点管理:合理管理焦点状态
响应式设计 
- 断点适配:在不同屏幕尺寸下调整显示方式
- 触摸友好:在移动设备上优化触摸体验
- 内容适配:确保内容在各种设备上都能正常显示
常见问题 
1. 自动播放不工作 
问题:走马灯不会自动切换
解决方案:
- 检查 autoplay属性是否设置为true
- 确认 interval时间间隔设置合理
- 检查是否有 JavaScript 错误阻止了执行
2. 图片显示异常 
问题:图片在走马灯中显示变形或不完整
解决方案:
- 使用 object-fit: cover保持图片比例
- 设置合适的容器高度
- 确保图片尺寸适当
3. 指示器位置问题 
问题:指示器显示位置不符合预期
解决方案:
- 检查 indicator-position属性设置
- 调整容器的 CSS 样式
- 确保有足够的空间显示指示器
4. 卡片模式显示问题 
问题:卡片模式下显示效果不佳
解决方案:
- 确保容器宽度足够
- 调整卡片内容的样式
- 检查是否有 CSS 冲突
总结 
Carousel 走马灯组件是一个功能丰富的轮播组件,支持多种显示模式和交互方式。通过合理配置属性和样式,可以创建出适合不同场景的轮播效果。在实际应用中,需要注意性能优化、用户体验和可访问性等方面的最佳实践。