Vue3走马灯(Carousel)

简介: 这是一个基于 Vue2 的走马灯(Carousel)组件,支持丰富的自定义配置。主要属性包括图片数组、宽度、高度、自动切换、暂停轮播、过渡效果、轮播间隔、箭头和指示点等。组件提供了多种过渡效果(如滑动和渐变)及动画时长设置,并允许自定义箭头和指示点的样式。此外,还支持通过键盘方向键进行切换,提供了灵活的使用方法。

可自定义设置以下属性:

  • 走马灯图片数组(images),类型:Array<{title: string, src: string , link?: string}>,必传,默认 []

  • 走马灯宽度(width),类型:number | string,单位 px,默认 '100%'

  • 走马灯高度(height),类型:number | string,单位 px,默认 '100vh'

  • 是否自动切换(autoplay),类型:boolean,默认 false

  • 当鼠标移入走马灯时,是否暂停自动轮播(pauseOnMouseEnter),类型:boolean,默认 false

  • 轮播图切换时的过渡效果(effect),类型:'slide' | 'fade',默认 'slide'

  • 自动轮播间隔(interval),类型:number,单位ms,默认 3000ms

  • 是否显示箭头(showArrow),类型:boolean,默认 true

  • 箭头颜色(arrowColor),类型:string,默认 '#FFF'

  • 箭头大小(arrowSize),类型:number,单位px,默认 36

  • 是否显示指示点(dots),类型:boolean,默认 true

  • 指示点大小(dotSize),类型:number,单位px,默认 10

  • 指示点颜色(dotColor),类型:string,默认 'rgba(255, 255, 255, 0.3)'

  • 指示点选中颜色(dotActiveColor),类型:string,默认 '#1677FF'

  • 指示点样式(dotStyle),优先级高于 dotSize、dotColor,类型:CSSProperties,默认 {}

  • 指示点位置(dotPosition),位置为 'left' | 'right' 时,effect: 'slide' 轮播自动变为垂直轮播,类型:'bottom'|'top'|'left'|'right',默认 'bottom'

  • 指示点触发切换的方式(dotsTrigger),类型:'click' | 'hover',默认 'click'

  • 图片加载中样式,Spin 组件属性配置(spinProps),参考 Spin Props,类型:object,默认 {}

  • 渐变动画持续时长(fadeDuration),类型:number,单位 ms,仅当 effect 为 'fade' 时生效,默认 500

  • 渐变动画函数(fadeFunction),类型:string,仅当 effect 为 'fade' 时生效,默认 'cubic-bezier(0.4, 0, 0.2, 1)',可参考 transition-timing-function

  • 滑动动画持续时长(slideDuration),类型:number,单位 ms,仅当 effect 为 'slide' 时生效,默认 1000

  • 滑动动画函数(slideFunction),类型:number[],仅当 effect 为 'slide' 时生效,默认 [0.65, 0, 0.35, 1],可参考 useTransition

效果如下图:在线预览

安装 @vueuse/core:

pnpm add @vueuse/core

①创建走马灯组件Carousel.vue:

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import type { CSSProperties } from 'vue'
import { rafTimeout, cancelRaf, useEventListener, useResizeObserver } from '../utils'
import { useTransition } from '@vueuse/core'
import Spin from '../spin'
interface Image {
  title?: string // 图片名称
  src: string // 图片地址
  link?: string // 图片跳转链接
}
interface Props {
  images?: Image[] // 走马灯图片数组
  width?: number | string // 走马灯宽度,单位 px
  height?: number | string // 走马灯高度,单位 px
  autoplay?: boolean // 是否自动轮播
  pauseOnMouseEnter?: boolean // 当鼠标移入走马灯时,是否暂停自动轮播
  effect?: 'slide' | 'fade' // 轮播图切换时的过渡效果
  interval?: number // 自动轮播间隔,单位 ms
  showArrow?: boolean // 是否显示箭头
  arrowColor?: string // 箭头颜色
  arrowSize?: number // 箭头大小,单位 px
  dots?: boolean // 是否显示指示点
  dotSize?: number // 指示点大小,单位 px
  dotColor?: string // 指示点颜色
  dotActiveColor?: string // 指示点选中颜色
  dotStyle?: CSSProperties // 指示点样式,优先级高于 dotSize、dotColor
  dotActiveStyle?: CSSProperties // 指示点选中样式,优先级高于 dotActiveColor
  dotPosition?: 'bottom' | 'top' | 'left' | 'right' // 指示点位置,位置为 'left' | 'right' 时,effect: 'slide' 轮播自动变为垂直轮播
  dotsTrigger?: 'click' | 'hover' // 指示点触发切换的方式
  spinProps?: object // 图片加载中样式,Spin 组件属性配置,参考 Spin Props
  fadeDuration?: number // 渐变动画持续时长,单位 ms,仅当 effect 为 'fade' 时生效
  fadeFunction?: string // 渐变动画函数,仅当 effect 为 'fade' 时生效,可参考 transition-timing-function 写法:https://developer.mozilla.org/zh-CN/docs/Web/CSS/transition-timing-function
  slideDuration?: number // 滑动动画持续时长,单位 ms,仅当 effect 为 'slide' 时生效
  slideFunction?: string | number[] // 滑动动画函数,仅当 effect 为 'slide' 时生效,可参考 useTransition 写法:https://vueuse.org/core/useTransition/#usage
}
const props = withDefaults(defineProps<Props>(), {
  images: () => [],
  width: '100%',
  height: '100vh',
  autoplay: false,
  pauseOnMouseEnter: false,
  effect: 'slide',
  interval: 3000,
  showArrow: true,
  arrowColor: '#FFF',
  arrowSize: 36,
  dots: true,
  dotSize: 10,
  dotColor: 'rgba(255, 255, 255, 0.3)',
  dotActiveColor: '#1677FF',
  dotStyle: () => ({}),
  dotActiveStyle: () => ({}),
  dotPosition: 'bottom',
  dotsTrigger: 'click',
  spinProps: () => ({}),
  fadeDuration: 500,
  fadeFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
  slideDuration: 800,
  slideFunction: () => [0.65, 0, 0.35, 1]
})
const offset = ref(0) // 滑动偏移值
const slideTimer = ref() // 轮播切换定时器
const stopCarousel = ref(false) // 鼠标悬浮时,停止切换标志
const switchPrevent = ref(false) // 在滑动切换过程中,禁用其他所有切换操作
const moveEffectRaf = ref() // 移动过程 requestAnimationFrame 的返回值,一个 long 整数,请求 ID,是回调列表中唯一的标识
const targetPosition = ref() // 目标移动位置
const carouselRef = ref() // carousel DOM 引用
const activeSwitcher = ref(1) // 当前展示图片标识
const imageWidth = ref() // 图片宽度
const imageHeight = ref() // 图片高度
const complete = ref(Array(props.images.length).fill(false)) // 图片是否加载完成
const carouselWidth = computed(() => {
  // 走马灯区域宽度
  if (typeof props.width === 'number') {
    return props.width + 'px'
  } else {
    return props.width
  }
})
const carouselHeight = computed(() => {
  // 走马灯区域高度
  if (typeof props.height === 'number') {
    return props.height + 'px'
  } else {
    return props.height
  }
})
const imageCount = computed(() => {
  // 轮播图片数量
  return props.images.length
})
const verticalSlide = computed(() => {
  // 是否垂直轮播
  return ['left', 'right'].includes(props.dotPosition)
})
const moveUnitDistance = computed(() => {
  // 每次移动的单位距离
  if (verticalSlide.value) {
    return imageHeight.value
  } else {
    return imageWidth.value
  }
})
const carouselStyle = computed(() => {
  if (props.effect === 'slide') {
    return {
      transform: (verticalSlide.value ? 'translateY' : 'translateX') + `(${-offset.value}px)`
    }
  } else {
    return {}
  }
})
watch(
  () => [
    verticalSlide.value,
    props.effect,
    props.images,
    props.autoplay,
    props.interval,
    props.fadeDuration,
    props.fadeFunction,
    complete.value[0]
  ],
  () => {
    initCarousel()
  },
  {
    deep: true,
    flush: 'post'
  }
)
const emits = defineEmits(['change', 'click'])
watch(activeSwitcher, (to) => {
  emits('change', to)
})
useEventListener(document, 'visibilitychange', visibilityChange)
useResizeObserver(carouselRef, () => {
  getImageSize()
  initCarousel()
})
function initCarousel() {
  slideTimer.value && cancelRaf(slideTimer.value)
  moveEffectRaf.value && cancelAnimationFrame(moveEffectRaf.value)
  switchPrevent.value = false
  if (props.effect === 'slide') {
    offset.value = (activeSwitcher.value - 1) * moveUnitDistance.value
  }
  onStart()
}
function onComplete(index: number) {
  // 图片加载完成
  complete.value[index] = true
}
function getImageSize() {
  // 获取每张图片大小
  imageWidth.value = carouselRef.value.offsetWidth
  imageHeight.value = carouselRef.value.offsetHeight
}
function onKeyboard(e: KeyboardEvent) {
  if (imageCount.value > 1) {
    if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
      onLeftArrow()
    }
    if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
      onRightArrow()
    }
  }
}
// 当用户导航到新页面、切换标签页、关闭标签页、最小化或关闭浏览器,或者在移动设备上从浏览器切换到不同的应用程序时,暂停切换
function visibilityChange() {
  console.log('visibilityState', document.visibilityState)
  const visibility = document.visibilityState
  if (visibility === 'hidden') {
    // hidden
    slideTimer.value && cancelRaf(slideTimer.value)
    offset.value = originNumber.value + distance.value
    switchPrevent.value = false
  } else {
    // visible
    onStart()
  }
}
function onStart() {
  if (props.autoplay && imageCount.value > 1 && complete.value[0]) {
    // 超过一条时滑动
    stopCarousel.value = false
    autoSlide() // 自动滑动轮播
    console.log('Carousel Start')
  }
}
function onStop() {
  slideTimer.value && cancelRaf(slideTimer.value)
  stopCarousel.value = true
  console.log('Carousel Stop')
}
function autoSlide() {
  if (!stopCarousel.value) {
    slideTimer.value && cancelRaf(slideTimer.value)
    slideTimer.value = rafTimeout(() => {
      switchPrevent.value = true // 禁用导航切换
      if (props.effect === 'slide') {
        const target = (offset.value % (imageCount.value * moveUnitDistance.value)) + moveUnitDistance.value
        moveLeft(target)
        activeSwitcher.value = (activeSwitcher.value % imageCount.value) + 1
      } else {
        // fade
        moveFade('left')
      }
    }, props.interval)
  }
}
function onLeftArrow() {
  if (!switchPrevent.value) {
    switchPrevent.value = true
    slideTimer.value && cancelRaf(slideTimer.value)
    if (props.effect === 'slide') {
      const target = ((activeSwitcher.value + imageCount.value - 2) % imageCount.value) * moveUnitDistance.value
      moveRight(target)
      activeSwitcher.value = activeSwitcher.value - 1 > 0 ? activeSwitcher.value - 1 : imageCount.value
    } else {
      // fade
      moveFade('right')
    }
  }
}
function onRightArrow() {
  if (!switchPrevent.value) {
    switchPrevent.value = true
    slideTimer.value && cancelRaf(slideTimer.value)
    if (props.effect === 'slide') {
      const target = activeSwitcher.value * moveUnitDistance.value
      moveLeft(target)
      activeSwitcher.value = (activeSwitcher.value % imageCount.value) + 1
    } else {
      // fade
      moveFade('left')
    }
  }
}
const baseNumber = ref(0)
const originNumber = ref(0) // 初始位置
const distance = ref(0) // 滑动距离
// @ts-ignore
const cubicBezierNumber = useTransition(baseNumber, {
  duration: props.slideDuration, // 过渡动画时长
  transition: props.slideFunction // 过渡动画函数
})
function moveFade(direction: 'left' | 'right' | 'switch', n?: number) {
  if (direction === 'left') {
    activeSwitcher.value = (activeSwitcher.value % imageCount.value) + 1
  } else if (direction === 'right') {
    activeSwitcher.value = activeSwitcher.value - 1 > 0 ? activeSwitcher.value - 1 : imageCount.value
  } else {
    activeSwitcher.value = n as number
  }
  rafTimeout(() => {
    switchPrevent.value = false
    if (props.autoplay) {
      autoSlide()
    }
  }, props.fadeDuration)
}
function toggleNumber(target: number) {
  targetPosition.value = target
  baseNumber.value = baseNumber.value ? 0 : 1
  originNumber.value = offset.value // 初始位置
  distance.value = target - originNumber.value // 总距离
}
function moveEffect() {
  // 滑动效果函数
  if (baseNumber.value) {
    offset.value = originNumber.value + distance.value * cubicBezierNumber.value
  } else {
    offset.value = originNumber.value + distance.value * (1 - cubicBezierNumber.value)
  }
}
function moveLeftEffect() {
  if (offset.value >= targetPosition.value) {
    switchPrevent.value = false
    if (props.autoplay) {
      autoSlide() // 自动间隔切换下一张
    }
  } else {
    moveEffect()
    moveEffectRaf.value = requestAnimationFrame(moveLeftEffect)
  }
}
function moveLeft(target: number) {
  // 箭头切换或跳转切换,向左滑动效果
  if (offset.value === imageCount.value * moveUnitDistance.value) {
    // 最后一张时,重置left
    offset.value = 0
  }
  toggleNumber(target)
  moveEffectRaf.value = requestAnimationFrame(moveLeftEffect)
}
function moveRightEffect() {
  if (offset.value <= targetPosition.value) {
    switchPrevent.value = false
    if (props.autoplay) {
      autoSlide()
    }
  } else {
    moveEffect()
    moveEffectRaf.value = requestAnimationFrame(moveRightEffect)
  }
}
function moveRight(target: number) {
  // 箭头切换或跳转切换,向右滑动效果
  if (offset.value === 0) {
    // 第一张时,重置left
    offset.value = imageCount.value * moveUnitDistance.value
  }
  toggleNumber(target)
  moveEffectRaf.value = requestAnimationFrame(moveRightEffect)
}
function onSwitch(n: number) {
  // 分页切换图片
  if (!switchPrevent.value && activeSwitcher.value !== n) {
    switchPrevent.value = true
    slideTimer.value && cancelRaf(slideTimer.value)
    if (n < activeSwitcher.value) {
      // 往右滑动
      if (props.effect === 'slide') {
        const target = (n - 1) * moveUnitDistance.value
        moveRight(target)
        activeSwitcher.value = n
      } else {
        // fade
        moveFade('switch', n)
      }
    }
    if (n > activeSwitcher.value) {
      // 往左滑动
      if (props.effect === 'slide') {
        const target = (n - 1) * moveUnitDistance.value
        moveLeft(target)
        activeSwitcher.value = n
      } else {
        // fade
        moveFade('switch', n)
      }
    }
  }
}
function onMouseEnter(n: number) {
  onSwitch(n)
}
function clickImage(image: Image) {
  emits('click', image)
}
function to(n: number): void {
  if (n >= 1 && n <= imageCount.value) {
    onSwitch(n)
  }
}
function prev(): void {
  onLeftArrow()
}
function next(): void {
  onRightArrow()
}
function getCurrentIndex(): number {
  return activeSwitcher.value
}
defineExpose({
  to,
  prev,
  next,
  getCurrentIndex
})
</script>
<template>
  <div
    ref="carouselRef"
    class="m-carousel"
    :class="{ 'carousel-vertical': verticalSlide, 'carousel-fade': effect === 'fade' }"
    :style="`--arrow-color: ${arrowColor}; --dot-size: ${dotSize}px; --dot-color: ${dotColor}; --fade-duration: ${props.fadeDuration}ms; --fade-function: ${props.fadeFunction}; width: ${carouselWidth}; height: ${carouselHeight};`"
    @mouseenter="autoplay && pauseOnMouseEnter ? onStop() : () => false"
    @mouseleave="autoplay && pauseOnMouseEnter ? onStart() : () => false"
  >
    <div class="m-carousel-flex" :style="carouselStyle">
      <div
        class="m-image"
        :class="{ 'image-fade-active': effect === 'fade' && activeSwitcher === index + 1 }"
        @click="clickImage(image)"
        v-for="(image, index) in images"
        :key="index"
      >
        <Spin :spinning="!complete[index]" indicator="dynamic-circle" v-bind="spinProps">
          <img
            @load="onComplete(index)"
            :src="image.src"
            :key="image.src"
            :alt="image.title"
            class="u-image"
            :style="`width: ${imageWidth}px; height: ${imageHeight}px;`"
          />
        </Spin>
      </div>
      <div class="m-image" @click="clickImage(images[0])" v-if="imageCount && effect === 'slide'">
        <Spin :spinning="!complete[0]" indicator="dynamic-circle" v-bind="spinProps">
          <img
            @load="onComplete(0)"
            :src="images[0].src"
            :key="images[0].src"
            :alt="images[0].title"
            class="u-image"
            :style="`width: ${imageWidth}px; height: ${imageHeight}px;`"
          />
        </Spin>
      </div>
    </div>
    <template v-if="showArrow">
      <svg
        tabindex="0"
        class="arrow-left"
        :style="`width: ${arrowSize}px; height: ${arrowSize}px;`"
        @click="onLeftArrow"
        @keydown.prevent="onKeyboard"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 16 16"
      >
        <path
          d="M10.26 3.2a.75.75 0 0 1 .04 1.06L6.773 8l3.527 3.74a.75.75 0 1 1-1.1 1.02l-4-4.25a.75.75 0 0 1 0-1.02l4-4.25a.75.75 0 0 1 1.06-.04z"
        ></path>
      </svg>
      <svg
        tabindex="0"
        class="arrow-right"
        :style="`width: ${arrowSize}px; height: ${arrowSize}px;`"
        @click="onRightArrow"
        @keydown.prevent="onKeyboard"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 16 16"
      >
        <path
          d="M5.74 3.2a.75.75 0 0 0-.04 1.06L9.227 8L5.7 11.74a.75.75 0 1 0 1.1 1.02l4-4.25a.75.75 0 0 0 0-1.02l-4-4.25a.75.75 0 0 0-1.06-.04z"
        ></path>
      </svg>
    </template>
    <div class="m-switch" :class="`switch-${dotPosition}`" v-if="dots">
      <div
        tabindex="0"
        class="u-dot"
        :style="[dotStyle, activeSwitcher === n ? { backgroundColor: dotActiveColor, ...dotActiveStyle } : {}]"
        v-for="n in imageCount"
        :key="n"
        @click="dotsTrigger === 'click' ? onSwitch(n) : () => false"
        @mouseenter="dotsTrigger === 'hover' ? onMouseEnter(n) : () => false"
        @keydown.prevent="onKeyboard"
      ></div>
    </div>
  </div>
</template>
<style lang="less" scoped>
.m-carousel {
  display: inline-block;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
  .m-carousel-flex {
    display: flex;
    width: 100%;
    height: 100%;
    // will-change: transform;
    .m-image {
      // 指定了 flex 元素的收缩规则。flex 元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值
      flex-shrink: 0; // 默认为 1,为 0 时不缩小
      display: inline-block;
      cursor: pointer;
      .u-image {
        display: inline-block;
        object-fit: cover;
        vertical-align: bottom; // 消除img标签底部的5px
      }
    }
  }
  &:hover {
    .arrow-left {
      opacity: 0.7;
      pointer-events: auto;
    }
    .arrow-right {
      opacity: 0.7;
      pointer-events: auto;
    }
  }
  .arrow-left {
    position: absolute;
    left: 6px;
    top: 50%;
    transform: translateY(-50%);
    fill: var(--arrow-color);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    outline: none;
    transition: opacity 0.3s;
    &:hover {
      opacity: 1;
    }
  }
  .arrow-right {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    fill: var(--arrow-color);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    outline: none;
    transition: opacity 0.3s;
    &:hover {
      opacity: 1;
    }
  }
  .m-switch {
    display: flex;
    justify-content: center;
    gap: 8px;
    position: absolute;
    z-index: 9;
    bottom: 12px;
    left: 50%;
    transform: translateX(-50%);
    height: auto;
    .u-dot {
      // flex: 0 1 auto;
      width: var(--dot-size);
      height: var(--dot-size);
      border-radius: var(--dot-size);
      background-color: var(--dot-color);
      cursor: pointer;
      outline: none;
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
  }
  .switch-top {
    top: 12px;
    bottom: auto;
  }
  .switch-left {
    left: 12px;
    right: auto;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    flex-direction: column;
  }
  .switch-right {
    right: 12px;
    left: auto;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    flex-direction: column;
  }
}
.carousel-vertical {
  .m-carousel-flex {
    flex-direction: column;
  }
  .arrow-left {
    top: 6px;
    left: 50%;
    transform: translateX(-50%) rotate(90deg);
  }
  .arrow-right {
    top: auto;
    bottom: 6px;
    left: 50%;
    transform: translateX(-50%) rotate(90deg);
  }
}
.carousel-fade {
  .m-image {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    transition-property: opacity;
    transition-duration: var(--fade-duration);
    transition-timing-function: var(--fade-function);
  }
  .image-fade-active {
    opacity: 1;
    pointer-events: auto;
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import Carousel from './Carousel.vue'
import { ref, reactive } from 'vue'

const images = ref([
  {
    title: 'image-1',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/1.jpg',
    link: ''
  },
  {
    title: 'image-2',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/2.jpg',
    link: ''
  },
  {
    title: 'image-3',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/3.jpg',
    link: ''
  },
  {
    title: 'image-4',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/4.jpg',
    link: ''
  },
  {
    title: 'image-5',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/5.jpg',
    link: ''
  },
  {
    title: 'image-6',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/6.jpg',
    link: ''
  },
  {
    title: 'image-7',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/7.jpg',
    link: ''
  },
  {
    title: 'image-8',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/8.jpg',
    link: ''
  },
  {
    title: 'image-9',
    src: 'https://cdn.jsdelivr.net/gh/themusecatcher/resources@0.0.5/9.jpg',
    link: ''
  }
])
const showArrow = ref(false)
const positionOptions = ref([
  {
    label: 'top',
    value: 'top'
  },
  {
    label: 'bottom',
    value: 'bottom'
  },
  {
    label: 'left',
    value: 'left'
  },
  {
    label: 'right',
    value: 'right'
  }
])
const dotPosition = ref('top')
const effectOptions = ref([
  {
    label: 'slide',
    value: 'slide'
  },
  {
    label: 'fade',
    value: 'fade'
  }
])
const effect = ref('fade')
const triggerOptions = ref([
  {
    label: 'click',
    value: 'click'
  },
  {
    label: 'hover',
    value: 'hover'
  }
])
const trigger = ref('hover')
function clickImage(image: object) {
  console.log('image', image)
}
function onChange(index: number) {
  console.log('change', index)
}
const carousel = ref()
const toIndex = ref(1)
const currentIndex = ref(1)
function getCurrentIndex() {
  currentIndex.value = carousel.value.getCurrentIndex()
}
const carouselConfig = reactive({
  autoplay: true,
  pauseOnMouseEnter: false,
  effect: 'slide',
  interval: 3000,
  showArrow: true,
  arrowColor: '#FFF',
  arrowSize: 36,
  dots: true,
  dotSize: 10,
  dotColor: 'rgba(255, 255, 255, 0.3)',
  dotActiveColor: '#1677FF',
  dotPosition: 'bottom',
  dotsTrigger: 'click',
  fadeDuration: 500,
  fadeFunction: 'cubic-bezier(0.4, 0, 0.2, 1)'
})
</script>
<template>
  <div>
    <h1>{
  { $route.name }} {
  { $route.meta.title }}</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <h3 class="mb10">当焦点在 Arrow 或 Dots 上时,可以通过键盘上、下、左、右按键切换</h3>
    <Carousel :images="images" :width="800" :height="450" @click="clickImage" />
    <h2 class="mt30 mb10">箭头</h2>
    <Space align="center"> showArrow: <Switch v-model="showArrow" /> </Space>
    <br />
    <br />
    <Carousel :images="images" :width="800" :height="450" :show-arrow="showArrow" @click="clickImage" />
    <h2 class="mt30 mb10">自动轮播</h2>
    <Carousel :images="images" :width="800" :height="450" autoplay @change="onChange" @click="clickImage" />
    <h2 class="mt30 mb10">指示点位置</h2>
    <Radio :options="positionOptions" v-model:value="dotPosition" button button-style="solid" />
    <br />
    <br />
    <Carousel :images="images" :width="800" :height="450" autoplay :dotPosition="dotPosition" />
    <h2 class="mt30 mb10">垂直</h2>
    <Carousel :images="images" :width="800" :height="450" autoplay dotPosition="right" />
    <h2 class="mt30 mb10">移入暂停</h2>
    <Carousel :images="images" :width="800" :height="450" autoplay pause-on-mouse-enter dotPosition="right" />
    <h2 class="mt30 mb10">过渡效果</h2>
    <Radio :options="effectOptions" v-model:value="effect" button button-style="solid" />
    <br />
    <br />
    <Carousel :images="images" :width="800" :height="450" :effect="effect" :fade-duration="1500" />
    <h2 class="mt30 mb10">鼠标经过指示点切换轮播图</h2>
    <Radio :options="triggerOptions" v-model:value="trigger" button button-style="solid" />
    <br />
    <br />
    <Carousel :images="images" :width="800" :height="450" :dots-trigger="trigger" />
    <h2 class="mt30 mb10">自定义滑动动画</h2>
    <Carousel :images="images" :width="800" :height="450" :slide-duration="800" :slide-function="[0.45, 1, 0.55, 1]" />
    <h2 class="mt30 mb10">自定义样式</h2>
    <Carousel
      :images="images"
      :width="800"
      :height="450"
      arrow-color="#13C2C2"
      :arrow-size="48"
      dot-active-color="#13C2C2"
      :dot-style="{ backgroundColor: '#FFF' }"
      :dot-active-style="{ width: '25px', backgroundColor: 'gold' }"
      :spin-style="{ indicator: 'dot', color: '#13C2C2' }"
    />
    <h2 class="mt30 mb10">使用 Carousel Methods</h2>
    <Space>
      <InputNumber :min="1" :max="images.length" v-model:value="toIndex" />
      <Button @click="carousel.to(toIndex)">跳转到</Button>
      <Button @click="carousel.prev()">前一页</Button>
      <Button @click="carousel.next()">后一页</Button>
      <Button @click="getCurrentIndex">获取当前页:{
  { currentIndex }}</Button>
    </Space>
    <br />
    <br />
    <Carousel ref="carousel" :images="images" :width="800" :height="450" />
    <h2 class="mt30 mb10">走马灯配置器</h2>
    <Flex gap="large" vertical>
      <Row :gutter="[24, 12]">
        <Col :span="6">
          <Space gap="small" vertical> autoplay:<Switch v-model="carouselConfig.autoplay" /> </Space>
        </Col>
        <Col :span="6">
          <Space gap="small" vertical>
            pauseOnMouseEnter:<Switch v-model="carouselConfig.pauseOnMouseEnter" />
          </Space>
        </Col>
        <Col :span="6">
          <Space gap="small" vertical>
            effect:<Radio :options="effectOptions" v-model:value="carouselConfig.effect" button button-style="solid" />
          </Space>
        </Col>
        <Col :span="6">
          <Flex vertical>
            interval:<Slider v-model:value="carouselConfig.interval" :min="100" :step="10" :max="10000" />
          </Flex>
        </Col>
        <Col :span="6">
          <Space gap="small" vertical> showArrow:<Switch v-model="carouselConfig.showArrow" /> </Space>
        </Col>
        <Col :span="6">
          <Flex gap="small" vertical>
            arrowColor:<Input v-model:value="carouselConfig.arrowColor" placeholder="arrowColor" />
          </Flex>
        </Col>
        <Col :span="6">
          <Flex vertical> arrowSize:<Slider v-model:value="carouselConfig.arrowSize" :min="1" /> </Flex>
        </Col>
        <Col :span="6"></Col>
        <Col :span="6">
          <Space gap="small" vertical> dots:<Switch v-model="carouselConfig.dots" /> </Space>
        </Col>
        <Col :span="6">
          <Flex vertical> dotSize:<Slider v-model:value="carouselConfig.dotSize" :min="4" :max="64" /> </Flex>
        </Col>
        <Col :span="6">
          <Flex gap="small" vertical> dotColor:<Input v-model:value="carouselConfig.dotColor" placeholder="dotColor" /> </Flex>
        </Col>
        <Col :span="6">
          <Flex gap="small" vertical>
            dotActiveColor:<Input v-model:value="carouselConfig.dotActiveColor" placeholder="dotActiveColor" />
          </Flex>
        </Col>
        <Col :span="12">
          <Space gap="small" vertical>
            dotPosition:
            <Radio :options="positionOptions" v-model:value="carouselConfig.dotPosition" button button-style="solid" />
          </Space>
        </Col>
        <Col :span="6">
          <Space gap="small" vertical>
            dotsTrigger:
            <Radio :options="triggerOptions" v-model:value="carouselConfig.dotsTrigger" button button-style="solid" />
          </Space>
        </Col>
        <Col :span="6"></Col>
        <Col :span="6">
          <Flex vertical>
            fadeDuration:<Slider v-model:value="carouselConfig.fadeDuration" :min="100" :step="10" :max="10000" />
          </Flex>
        </Col>
        <Col :span="6">
          <Flex gap="small" vertical>
            fadeFunction:<Input v-model:value="carouselConfig.fadeFunction" placeholder="fadeFunction" />
          </Flex>
        </Col>
      </Row>
      <Carousel
        :images="images"
        :height="450"
        :autoplay="carouselConfig.autoplay"
        :pause-on-mouse-enter="carouselConfig.pauseOnMouseEnter"
        :effect="carouselConfig.effect"
        :interval="carouselConfig.interval"
        :show-arrow="carouselConfig.showArrow"
        :arrow-color="carouselConfig.arrowColor"
        :arrow-size="carouselConfig.arrowSize"
        :dots="carouselConfig.dots"
        :dot-size="carouselConfig.dotSize"
        :dot-color="carouselConfig.dotColor"
        :dot-active-color="carouselConfig.dotActiveColor"
        :dot-position="carouselConfig.dotPosition"
        :dots-trigger="carouselConfig.dotsTrigger"
        :fade-duration="carouselConfig.fadeDuration"
        :fade-function="carouselConfig.fadeFunction"
        :spin-style="{ indicator: 'dot', color: '#13C2C2' }"
      />
    </Flex>
  </div>
</template>
相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
17天前
vue3学习(3)
vue3学习(3)
|
14天前
|
JavaScript API
Vue3中的计算属性能否动态修改
【9月更文挑战第5天】Vue3中的计算属性能否动态修改
49 10
|
7天前
|
JavaScript
Vue3中路由跳转的语法
Vue3中路由跳转的语法
111 58
|
9天前
|
前端开发
vue3+ts项目中使用mockjs
vue3+ts项目中使用mockjs
209 58
|
5天前
|
JavaScript 索引
Vue 2和Vue 3的区别以及实现原理
Vue 2 的响应式系统通过Object.defineProperty来实现,它为对象的每个属性添加 getter 和 setter,以便追踪依赖并响应数据变化。
20 9
|
7天前
|
JavaScript 开发工具
vite如何打包vue3插件为JSSDK
【9月更文挑战第10天】以下是使用 Vite 打包 Vue 3 插件为 JS SDK 的步骤:首先通过 `npm init vite-plugin-sdk --template vue` 创建 Vue 3 项目并进入项目目录 `cd vite-plugin-sdk`。接着,在 `src` 目录下创建插件文件(如 `myPlugin.js`),并在 `main.js` 中引入和使用该插件。然后,修改 `vite.config.js` 文件以配置打包选项。最后,运行 `npm run build` 进行打包,生成的 `my-plugin-sdk.js` 即为 JS SDK,可在其他项目中引入使用。
|
7天前
|
JavaScript 开发者
彻底搞懂 Vue3 中 watch 和 watchEffect是用法
彻底搞懂 Vue3 中 watch 和 watchEffect是用法
|
14天前
|
JavaScript API
如何使用Vue3的可计算属性
【9月更文挑战第5天】如何使用Vue3的可计算属性
44 13
|
5天前
|
JavaScript 调度
Vue3 使用 Event Bus
Vue3 使用 Event Bus
10 1
|
5天前
|
JavaScript
Vue3 : ref 与 reactive
Vue3 : ref 与 reactive
9 1