Vue2走马灯扩展版(Carousel)

简介: 这篇文章介绍了如何在Vue 3框架中创建一个可自定义的走马灯(Carousel)组件,支持自动播放、导航、分页和响应用户交互等功能。

可自定义设置以下属性:

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

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

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

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

  • 是否显示导航(navigation),默认 true

  • 是否显示分页(pagination),默认 true

  • 用户操作导航或分页之后,是否禁止自动切换(disableOnInteraction),默认 true

  • 鼠标悬浮时暂停自动切换,鼠标离开时恢复自动切换(pauseOnMouseEnter),默认true

效果如下图:(Vue2走马灯的扩展版)

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

<template>
  <div
    class="m-slider"
    ref="carousel"
    :style="`width: ${carouselWidth}; height: ${carouselHeight};`"
    @mouseenter="pauseOnMouseEnter ? onStop() : e => e.preventDefault()"
    @mouseleave="pauseOnMouseEnter ? onStart() : e => e.preventDefault()">
    <div :class="{'transition': transition}" :style="`width: ${totalWidth}px; height: 100%; will-change: transform; transform: translateX(${-left}px);`">
      <div
        v-for="(image, index) in imageData"
        :key="index"
        class="m-image">
        <a :href="image.link ? image.link:'javascript:;'" :target="image.link ? '_blank':'_self'" class="m-link">
          <img v-lazy="getDefault(image.imgUrl)" :key="image.imgUrl" :alt="image.title" class="u-img" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`"/>
        </a>
      </div>
      <div class="m-image" v-if="len">
        <a :href="imageData[0].link ? imageData[0].link:'javascript:;'" :target="imageData[0].link ? '_blank':'_self'" class="m-link">
          <img v-lazy="getDefault(imageData[0].imgUrl)" :key="imageData[0].imgUrl" :alt="imageData[0].title" class="u-img"  :style="`width: ${imageWidth}px; height: ${imageHeight}px;`"/>
        </a>
      </div>
    </div>
    <template v-if="navigation">
      <svg class="arrow-left" @click="onLeftArrow((activeSwitcher + len - 2)%len*imageWidth)" viewBox="64 64 896 896" data-icon="left-circle" aria-hidden="true" focusable="false"><path d="M603.3 327.5l-246 178a7.95 7.95 0 0 0 0 12.9l246 178c5.3 3.8 12.7 0 12.7-6.5V643c0-10.2-4.9-19.9-13.2-25.9L457.4 512l145.4-105.2c8.3-6 13.2-15.6 13.2-25.9V334c0-6.5-7.4-10.3-12.7-6.5z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path></svg>
      <svg class="arrow-right" @click="onRightArrow(activeSwitcher*imageWidth)" viewBox="64 64 896 896" data-icon="right-circle" aria-hidden="true" focusable="false"><path d="M666.7 505.5l-246-178A8 8 0 0 0 408 334v46.9c0 10.2 4.9 19.9 13.2 25.9L566.6 512 421.2 617.2c-8.3 6-13.2 15.6-13.2 25.9V690c0 6.5 7.4 10.3 12.7 6.5l246-178c4.4-3.2 4.4-9.8 0-13z"></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path></svg>
    </template>
    <div class="m-switch" v-if="pagination">
      <div
        @click="onSwitch(n)"
        :class="['u-rect', {'active': activeSwitcher === n }]"
        v-for="n in len"
        :key="n">
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyLoad) // 图片懒加载插件
export default {
  name: 'Carousel',
  props: {
    imageData: { // 走马灯图片数组
      type: Array,
      default: () => []
    },
    interval: { // 自动滑动轮播间隔
      type: Number,
      default: 3000
    },
    width: { // 走马灯宽度
      type: [Number, String],
      default: '100%'
    },
    height: { // 走马灯高度
      type: [Number, String],
      default: '100vh'
    },
    navigation: { // 是否显示导航
      type: Boolean,
      default: true
    },
    pagination: { // 是否显示分页
      type: Boolean,
      default: true
    },
    disableOnInteraction: { // 用户操作导航或分页之后,是否禁止自动切换。默认为true:停止。
      type: Boolean,
      default: true
    },
    pauseOnMouseEnter: { // 鼠标悬浮时暂停自动切换,鼠标离开时恢复自动切换,默认true
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      toLeft: true, // 左滑标志,默认左滑
      left: 0, // 滑动偏移值
      transition: false, // 暂停时为完成滑动的过渡标志
      slideTimer: null, // 轮播切换定时器
      moveRaf: null, // 滑动效果回调标识
      targetMove: null, // 目标移动位置
      switched: false, // 是否在进行跳转切换,用于区别箭头或自动切换(false)和跳转切换(true)
      activeSwitcher: 1, // 当前展示图片标识
      fpsRaf: null, // fps回调标识
      fps: 60,
      imageWidth: null, // 图片宽度
      imageHeight: null // 图片高度
    }
  },
  computed: {
    carouselWidth () { // 走马灯区域宽度
      if (typeof this.width === 'number') {
        return this.width + 'px'
      } else {
        return this.width
      }
    },
    carouselHeight () { // 走马灯区域高度
      if (typeof this.height === 'number') {
        return this.height + 'px'
      } else {
        return this.height
      }
    },
    totalWidth () { // 容器宽度:(图片数组长度+1) * 图片宽度
      return (this.imageData.length + 1) * this.imageWidth
    },
    len () { // 图片数量
      return this.imageData.length
    },
    step () { // 移动参数(120fps: 24, 60fps: 12)
      if (this.fps === 60) {
        return 12
      } else {
        return 12 * (this.fps / 60)
      }
    }
  },
  mounted () {
    this.getFPS() // 获取浏览器的刷新率
    this.getImageSize() // 获取每张图片大小
  },
  methods: {
    getDefault (src) { // 获取懒加载默认图
      return {
        src: src,
        error: require('../assets/images/default.png'),
        loading: require('../assets/images/default.png')
      }
    },
    getFPS () { // 获取屏幕刷新率
      const requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
      var start = null
      const that = this
      function timeElapse (timestamp) {
        /*
          timestamp参数:与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻
        */
        // console.log('timestamp:', timestamp)
        if (!start) {
          if (that.fpsRaf > 10) {
            start = timestamp
          }
          that.fpsRaf = requestAnimationFrame(timeElapse)
        } else {
          that.fps = Math.floor(1000 / (timestamp - start))
          console.log('fps', that.fps)
          that.onStart()
        }
      }
      this.fpsRaf = requestAnimationFrame(timeElapse)
    },
    getImageSize () {
      this.imageWidth = this.$refs.carousel.offsetWidth
      this.imageHeight = this.$refs.carousel.offsetHeight
    },
    onStart () {
      if (this.len > 1) { // 超过一条时滑动
        this.toLeft = true // 重置左滑标志
        this.transition = false
        this.onAutoSlide() // 自动滑动轮播
        console.log('imageSlider start')
      }
    },
    onStop () {
      clearTimeout(this.slideTimer)
      if (this.toLeft) { // 左滑箭头移出时
        this.onStopLeft()
      } else {
        this.onStopRight()
      }
      console.log('imageSlider stop')
    },
    onStopLeft () { // 停止往左滑动
      clearTimeout(this.slideTimer)
      cancelAnimationFrame(this.moveRaf)
      this.transition = true
      this.left = Math.ceil(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整
    },
    onStopRight () { // 停止往右滑动
      cancelAnimationFrame(this.moveRaf)
      this.transition = true
      this.left = Math.floor(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整
    },
    onAutoSlide () {
      this.slideTimer = setTimeout(() => {
        // 或者使用 this.activeSwitcher % (this.len + 1) * this.imageWidth
        const target = this.left % (this.len * this.imageWidth) + this.imageWidth
        this.activeSwitcher = this.activeSwitcher % this.len + 1
        this.autoMoveLeft(target)
      }, this.interval)
    },
    goLeft (target) { // 点击右箭头,往左滑动
      if (this.toLeft) {
        this.onStopLeft()
      } else {
        this.onStopRight()
        this.toLeft = true // 向左滑动
      }
      this.transition = false
      this.moveLeft(target)
    },
    goRight (target) { // 点击左箭头,往右滑动
      if (this.toLeft) {
        this.onStopLeft()
        this.toLeft = false // 非向左滑动
      } else {
        this.onStopRight()
      }
      this.transition = false
      this.moveRight(target)
    },
    onLeftArrow (target) {
      this.activeSwitcher = (this.activeSwitcher - 1 > 0) ? this.activeSwitcher - 1 : this.len
      this.goRight(target)
    },
    onRightArrow (target) {
      this.activeSwitcher = this.activeSwitcher % this.len + 1
      this.goLeft(target)
    },
    autoMoveLeftEffect () {
      if (this.left >= this.targetMove) {
        this.onAutoSlide() // 自动间隔切换下一张
      } else {
        var move = Math.ceil((this.targetMove - this.left) / this.step) // 越来越慢的滑动过程
        this.left += move
        this.moveRaf = requestAnimationFrame(this.autoMoveLeftEffect)
      }
    },
    autoMoveLeft (target) { // 自动切换,向左滑动效果
      if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left
        this.left = 0
      }
      this.targetMove = target
      this.moveRaf = requestAnimationFrame(this.autoMoveLeftEffect)
    },
    moveLeftEffect () {
      if (this.left >= this.targetMove) {
        if (this.switched) { // 跳转切换,完成后自动滑动
          this.switched = false
          if (!this.disableOnInteraction && !this.pauseOnMouseEnter) {
            this.onStart()
          }
        }
      } else {
        var move = Math.ceil((this.targetMove - this.left) / this.step) // 越来越慢的滑动过程
        this.left += move
        this.moveRaf = requestAnimationFrame(this.moveLeftEffect)
      }
    },
    moveLeft (target) { // 箭头切换或跳转切换,向左滑动效果
      if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left
        this.left = 0
      }
      this.targetMove = target
      this.moveRaf = requestAnimationFrame(this.moveLeftEffect)
    },
    moveRightEffect () {
      if (this.left <= this.targetMove) {
        if (this.switched) { // 跳转切换,完成后自动滑动
          this.switched = false
          if (!this.disableOnInteraction && !this.pauseOnMouseEnter) {
            this.onStart()
          }
        }
      } else {
        var move = Math.floor((this.targetMove - this.left) / this.step) // 越来越慢的滑动过程
        this.left += move
        this.moveRaf = requestAnimationFrame(this.moveRightEffect)
      }
    },
    moveRight (target) { // 箭头切换或跳转切换,向右滑动效果
      if (this.left === 0) { // 第一张时,重置left
        this.left = this.len * this.imageWidth
      }
      this.targetMove = target
      this.moveRaf = requestAnimationFrame(this.moveRightEffect)
    },
    onSwitch (n) { // 分页切换图片
      if (this.activeSwitcher !== n) {
        this.switched = true // 跳转切换标志
        const target = (n - 1) * this.imageWidth
        if (n < this.activeSwitcher) { // 往右滑动
          this.activeSwitcher = n
          this.goRight(target)
        }
        if (n > this.activeSwitcher) { // 往左滑动
          this.activeSwitcher = n
          this.goLeft(target)
        }
      }
    },
    beforeDestroy () {
      clearTimeout(this.slideTimer)
      this.slideTimer = null
    }
  }
}
</script>
<style lang="less" scoped>
@themeColor: #1890FF;
.m-slider {
  display: inline-block;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
  .transition {
    transition: transform 0.3s ease-out;
  }
  .m-image {
    display: inline-block;
    .m-link {
      display: block;
      height: 100%;
      .u-img {
        object-fit: cover;
        vertical-align: bottom; // 消除img标签底部的5px
        cursor: pointer;
      }
    }
  }
  &:hover {
    .arrow-left {
      opacity: 1;
      pointer-events: auto;
    }
    .arrow-right {
      opacity: 1;
      pointer-events: auto;
    }
  }
  .arrow-left {
    width: 28px;
    height: 28px;
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    fill: rgba(255, 255, 255, .6);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: all .3s;
    &:hover {
      fill: rgba(255, 255, 255);
    }
  }
  .arrow-right {
    width: 28px;
    height: 28px;
    position: absolute;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    fill: rgba(255, 255, 255, .6);
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: all .3s;
    &:hover {
      fill: rgba(255, 255, 255);
    }
  }
  .m-switch {
    position: absolute;
    width: 100%;
    text-align: center;
    bottom: 8px;
    .u-rect {
      display: inline-block;
      vertical-align: middle;
      width: 36px;
      height: 4px;
      background: #E3E3E3;
      border-radius: 1px;
      margin: 0 4px;
      cursor: pointer;
      transition: background-color 0.3s;
    }
    .active {
      background-color: @themeColor;
    }
  }
}
</style>

②在要使用的页面引入:

<Carousel
    :imageData="imageData"
    :width="800"
    :height="450"
    :interval="1500"
    :pauseOnMouseEnter="true"
    :disableOnInteraction="false" />
import Carousel from '@/components/Carousel'
components: {
    Carousel
}
data () {
    return {
      imageData: [
        {
          title: 'image-1,image-1,image-1,image-1,image-1,image-1,image-1,image-1,image-1',
          imgUrl: 'image src...'
        },
        {
          title: 'image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2,image-2',
          imgUrl: 'image src...'
        },
        {
          title: 'image-3,image-3,image-3,image-3,image-3,image-3,image-3,image-3,image-3',
          imgUrl: 'image src...'
        }
      ]
   }
}
相关文章
|
6月前
|
JavaScript
【vue】 vue-seamless-scroll 无缝滚动依赖
【vue】 vue-seamless-scroll 无缝滚动依赖
566 1
|
2月前
|
JavaScript 前端开发
vue动态添加style样式
vue动态添加style样式
331 61
|
21天前
|
JavaScript API
|
3月前
Vue3走马灯(Carousel)
这是一个基于 Vue2 的走马灯(Carousel)组件,支持丰富的自定义配置。主要属性包括图片数组、宽度、高度、自动切换、暂停轮播、过渡效果、轮播间隔、箭头和指示点等。组件提供了多种过渡效果(如滑动和渐变)及动画时长设置,并允许自定义箭头和指示点的样式。此外,还支持通过键盘方向键进行切换,提供了灵活的使用方法。
Vue3走马灯(Carousel)
|
3月前
Vue2走马灯(Carousel)
这是一个基于 Vue2 的走马灯轮播组件(Carousel),支持鼠标移入暂停、移出后自动轮播。用户可以自定义设置轮播图片数组、滑动间隔时间、图片宽高。该组件提供了两种滑动效果实现方式:一种是通过 `setInterval` 延时调用,另一种是使用 `requestAnimationFrame()`,后者效果更佳。组件还支持图片懒加载,适用于多种应用场景。
187 0
Vue2走马灯(Carousel)
|
3月前
|
JavaScript
Vue3文字滚动(TextScroll)
这是一个可定制的文字滚动组件,支持水平和垂直滚动。主要属性包括滚动文字数组 `scrollText`、是否启用单条文字滚动 `single`、滚动区域宽高 `width` 和 `height`、滚动区域和文字样式 `boardStyle` 和 `textStyle`、滚动条数 `amount`、间距 `gap`、动画间隔 `interval` 和 `step`、以及垂直滚动时间间隔 `verticalInterval`。组件内置多种样式调整功能,并提供在线预览示例。
Vue3文字滚动(TextScroll)
|
3月前
|
JavaScript
Vue2横向文字滚动
这篇文章介绍了如何在Vue 2框架中实现一个横向滚动文本的组件,允许自定义滚动文本内容、滚动区域尺寸和滚动速度等属性。
Vue2横向文字滚动
|
3月前
在Vue3项目中使用 vue3-seamless-scroll 无缝滚动插件
本文介绍了如何在Vue3项目中使用`vue3-seamless-scroll`插件实现无缝滚动效果,并提供了详细的示例代码和运行效果。
1267 0
|
5月前
Vue3——tdesign-vue-next如何按需加载动态渲染ICON
如题,在vue3中进行按需加载来动态的渲染icon图标;
58 1
|
6月前
|
JavaScript 前端开发 UED
教你用vue自定义指令做一个组件的遮罩层loading效果
教你用vue自定义指令做一个组件的遮罩层loading效果
526 0