Vue2走马灯(Carousel)

简介: 这是一个基于 Vue2 的走马灯轮播组件(Carousel),支持鼠标移入暂停、移出后自动轮播。用户可以自定义设置轮播图片数组、滑动间隔时间、图片宽高。该组件提供了两种滑动效果实现方式:一种是通过 `setInterval` 延时调用,另一种是使用 `requestAnimationFrame()`,后者效果更佳。组件还支持图片懒加载,适用于多种应用场景。

移入时暂停,移出后自动轮播

可自定义设置以下属性:

  • 轮播图片数组(imageData),默认[]
  • 滑动轮播间隔(interval),默认3000ms
  • 图片宽度(imageWidth),默认400px
  • 图片高度(imageHeight),默认300px

共使用两种滑动效果实现方式:

  • 延时调用setInterval(setTimeout类似)
  • requestAnimationFrame()(效果更好)

效果如下图:

①创建图片轮播组件Carousel.vue:

  • 滑动效果使用setInterval延时调用**(使用setTimeout超时调用类似)**
<template>
  <!-- 方法1:使用 will-change: transform; translateX(-${left}px); -->
  <!-- <div class="m-slider" :style="`width: ${imageWidth}px;`" @mouseenter="onStop" @mouseleave="onStart">
    <div :class="{ 'transition': transition }" :style="`width: ${width}px; will-change: transform; transform: translateX(${-left}px);`"> -->
  <!-- 方法2:使用position: relative;和position: absolute; left: ${-left}px; -->
  <div class="m-slider" :style="`position: relative; width: ${imageWidth}px; height: ${imageHeight + 36}px;`" @mouseenter="onStop" @mouseleave="onStart">
    <div :class="{ 'transition': transition }" :style="`width: ${width}px; position: absolute; left: ${-left}px;`">
      <div
        v-for="(item, index) in imageData"
        :key="index"
        :style="`width: ${imageWidth}px;`"
        class="m-image">
        <img v-lazy="getDefault(item.imgUrl)" :alt="item.title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
        <p class="u-img-title" :title="item.title">{
  { item.title }}</p>
      </div>
      <div class="m-image" :style="`width: ${imageWidth}px;`">
        <img v-lazy="getDefault(imageData[0].imgUrl)" :alt="imageData[0].title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
        <p class="u-img-title" :title="imageData[0].title">{
  { imageData[0].title }}</p>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from 'vue'
import VueLazyLoad from 'vue-lazyload' // 图片懒加载插件使用版本v1.3.3
Vue.use(VueLazyLoad)
export default {
  name: 'Carousel',
  props: {
    imageData: { // 轮播图片数组
      type: Array,
      default: () => {
        return []
      }
    },
    interval: { // 滑动轮播间隔
      type: Number,
      default: 3000
    },
    imageWidth: { // 图片宽度
      type: Number,
      default: 400
    },
    imageHeight: { // 图片高度
      type: Number,
      default: 300
    }
  },
  data () {
    return {
      left: 0, // 滑动偏移值
      transition: false, // 暂停时未完成滑动的过渡标志
      slideTimer: null, // 自动切换定时器
      moveTimer: null // 向左滑动定时器
    }
  },
  computed: {
    width () { // 容器宽度:(图片数组长度+1) * 图片宽度
      return (this.imageData.length + 1) * this.imageWidth
    },
    len () {
      return this.imageData.length || 0
    }
  },
  mounted () {
    window.onfocus = () => { // 页面激活状态
      this.onStart()
    }
    window.onblur = () => { // 页面未激活状态
      this.onStop()
    }
    this.onStart()
  },
  methods: {
    getDefault (src) { // 获取懒加载默认图
      return {
        src: src,
        error: require('../assets/images/default.png'),
        loading: require('../assets/images/default.png')
      }
    },
    onStart () {
      if (this.len > 1) { // 超过一条时滑动
        this.transition = false
        this.onAutoSlide() // 自动滑动轮播
        console.log('imageSlider start')
      }
    },
    onStop () {
      clearTimeout(this.slideTimer)
      clearInterval(this.moveTimer)
      this.sliderTimer = null
      this.moveTimer = null
      this.transition = true
      this.left = Math.ceil(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整
      console.log('imageSlider stop')
    },
    onAutoSlide () {
      this.slideTimer = setTimeout(() => {
        const target = this.left % (this.len * this.imageWidth) + this.imageWidth
        this.autoMoveLeft(target)
      }, this.interval)
    },
    // 滑动使用setInterval延时调用
    autoMoveLeft (target) { // 自动切换,向左滑动效果
      if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left
        this.left = 0
      }
      this.moveTimer = setInterval(() => {
        if (this.left >= target) {
          clearInterval(this.moveTimer)
          this.moveTimer = null
          this.onAutoSlide() // 自动间隔切换下一张
        } else {
          var step = Math.ceil((target - this.left) / 10) // 越来越慢的滑动过程
          this.left += step
        }
      }, 25)
    },
    beforeDestroy () {
      clearTimeout(this.slideTimer)
      clearInterval(this.moveTimer)
      this.slideTimer = null
      this.moveTimer = null
    }
  }
}
</script>
<style lang="less" scoped>
@themeColor: #1890FF;
.m-slider {
  margin: 0 auto;
  overflow: hidden;
  .transition {
    transition: transform 0.3s ease-out;
  }
  .m-image {
    display: inline-block;
    .u-img {
      vertical-align: bottom; // 消除img标签底部的5px
      cursor: pointer;
    }
    .u-img-title {
      font-size: 18px;
      color: #333;
      line-height: 36px;
      text-align: left;
      cursor: pointer;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      &:hover {
        color: @themeColor;
      }
    }
  }
}
</style>
  • 滑动效果使用requestAnimationFrame**()**
  • <template>
      <div class="m-slider" :style="`width: ${imageWidth}px;`" @mouseenter="onStop" @mouseleave="onStart">
        <div :class="{ 'transition': transition }" :style="`width: ${width}px; will-change: transform; transform: translateX(${-left}px);`">
          <div
            v-for="(item, index) in imageData"
            :key="index"
            :style="`width: ${imageWidth}px;`"
            class="m-image">
            <img v-lazy="getDefault(item.imgUrl)" :alt="item.title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
            <p class="u-img-title" :title="item.title">{
        { item.title }}</p>
          </div>
          <div class="m-image" :style="`width: ${imageWidth}px;`">
            <img v-lazy="getDefault(imageData[0].imgUrl)" :alt="imageData[0].title" :style="`width: ${imageWidth}px; height: ${imageHeight}px;`" class="u-img"/>
            <p class="u-img-title" :title="imageData[0].title">{
        { imageData[0].title }}</p>
          </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: () => {
            return []
          }
        },
        interval: { // 滑动轮播间隔
          type: Number,
          default: 3000
        },
        imageWidth: { // 图片宽度
          type: Number,
          default: 400
        },
        imageHeight: { // 图片高度
          type: Number,
          default: 300
        }
      },
      data () {
        return {
          left: 0, // 滑动偏移值
          transition: false, // 暂停时未完成滑动的过渡标志
          slideTimer: null, // 自动切换定时器
          moveRaf: null, // 动画回调标识
          target: null, // 要移动到的目标位置
          start: 0,
          end: 0,
          fpsRaf: null, // fps回调标识
          step: 15 // 默认移动参数,对应60fps
        }
      },
      computed: {
        width () { // 容器宽度:(图片数组长度+1) * 图片宽度
          return (this.imageData.length + 1) * this.imageWidth
        },
        len () {
          return this.imageData.length || 0
        }
      },
      mounted () {
        window.onfocus = () => { // 页面激活状态
          this.onStart()
        }
        window.onblur = () => { // 页面未激活状态
          this.onStop()
        }
        this.fpsRaf = requestAnimationFrame(this.getFPS) // 获取浏览器的刷新率
      },
      methods: {
        getDefault (src) { // 获取懒加载默认图
          return {
            src: src,
            error: require('../assets/images/default.png'),
            loading: require('../assets/images/default.png')
          }
        },
        getFPS (timestamp) {
          // 单位ms,用1000ms/两个时间的间隔≈刷新频率fps
          // console.log('timestamp:', timestamp)
          if (this.fpsRaf === 2) {
            this.start = timestamp
          }
          if (this.fpsRaf === 3) {
            this.end = timestamp
            const fps = Math.floor(1000 / (this.end - this.start))
            if (fps === 120) {
              this.step = 30
            }
          }
          this.fpsRaf = requestAnimationFrame(this.getFPS)
          if (this.fpsRaf > 3) {
            cancelAnimationFrame(this.fpsRaf)
            this.onStart()
          }
        },
        onStart () {
          if (this.len > 1) { // 超过一条时滑动
            this.transition = false
            this.onAutoSlide() // 自动滑动轮播
            console.log('imageSlider start')
          }
        },
        onStop () {
          clearTimeout(this.slideTimer)
          this.slideTimer = null
          cancelAnimationFrame(this.moveRaf)
          this.transition = true
          this.left = Math.ceil(this.left / this.imageWidth) * this.imageWidth // ceil:向上取整,floor:向下取整
          console.log('imageSlider stop')
        },
        onAutoSlide () {
          this.slideTimer = setTimeout(() => {
            const target = this.left % (this.len * this.imageWidth) + this.imageWidth
            this.autoMoveLeft(target)
          }, this.interval)
        },
        // 滑动效果使用requestAnimationFrame
        autoMoveLeft (target) { // 自动切换,向左滑动效果
          if (this.left === this.len * this.imageWidth) { // 最后一张时,重置left
            this.left = 0
          }
          this.target = target
          this.moveRaf = requestAnimationFrame(this.autoLeftSlideEffect)
        },
        autoLeftSlideEffect () { // 自动向左滑动效果
          if (this.left >= this.target) {
            cancelAnimationFrame(this.moveRaf)
            this.onAutoSlide() // 自动间隔切换下一张
          } else {
            const move = Math.ceil((this.target - this.left) / this.step)
            this.left += move
            this.moveRaf = requestAnimationFrame(this.autoLeftSlideEffect)
          }
        },
        beforeDestroy () {
          clearTimeout(this.slideTimer)
          this.slideTimer = null
        }
      }
    }
    </script>
    <style lang="less" scoped>
    @themeColor: #1890FF;
    .m-slider {
      margin: 100px auto;
      overflow: hidden;
      .transition {
        transition: transform 0.3s ease-out;
      }
      .m-image {
        display: inline-block;
        .u-img {
          vertical-align: bottom; // 消除img标签底部的5px
          cursor: pointer;
        }
        .u-img-title {
          font-size: 18px;
          color: #333;
          line-height: 36px;
          text-align: left;
          cursor: pointer;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
          &:hover {
            color: @themeColor;
          }
        }
      }
    }
    </style>
    

    ②在要使用滑动轮播图片的页面引入使用:

  • <Carousel :imageData="imageData" :imageWidth="460" :imageHeight="320" :interval="3000" />
    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',
              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...'
            }
          ]
       }
    }
    
相关文章
|
4月前
|
JavaScript
vue element plus Badge 徽章
vue element plus Badge 徽章
68 0
|
4月前
|
JavaScript
vue element plus ColorPicker 颜色选择器
vue element plus ColorPicker 颜色选择器
119 0
|
21天前
|
JavaScript
Vue2面包屑(Breadcrumb)
这篇文章介绍了如何在Vue 3框架中创建一个面包屑(Breadcrumb)组件,支持自定义路由数组、面包屑高度和分隔符。
Vue2面包屑(Breadcrumb)
|
21天前
Vue3走马灯(Carousel)
这是一个基于 Vue2 的走马灯(Carousel)组件,支持丰富的自定义配置。主要属性包括图片数组、宽度、高度、自动切换、暂停轮播、过渡效果、轮播间隔、箭头和指示点等。组件提供了多种过渡效果(如滑动和渐变)及动画时长设置,并允许自定义箭头和指示点的样式。此外,还支持通过键盘方向键进行切换,提供了灵活的使用方法。
Vue3走马灯(Carousel)
|
21天前
Vue3面包屑(Breadcrumb)
该Breadcrumb组件允许自定义设置多个属性,包括路由数组、面包屑类名和样式、文本最大显示宽度、分隔符及样式、以及目标URL的打开方式。通过这些配置项,可以轻松实现不同样式的面包屑导航。组件支持点击跳转,并且能够处理带查询参数的路径。在线预览展示了其丰富的定制功能。可通过引入并在页面中使用该组件来快速构建导航结构。
Vue3面包屑(Breadcrumb)
|
20天前
Vue3徽标(Badge)
该组件库包含 `Descriptions` 和 `DescriptionsItem` 两种组件,需配合使用。
Vue3徽标(Badge)
|
21天前
|
JavaScript
Vue2走马灯扩展版(Carousel)
这篇文章介绍了如何在Vue 3框架中创建一个可自定义的走马灯(Carousel)组件,支持自动播放、导航、分页和响应用户交互等功能。
Vue2走马灯扩展版(Carousel)
|
20天前
|
JavaScript
Vue3文字滚动(TextScroll)
这是一个可定制的文字滚动组件,支持水平和垂直滚动。主要属性包括滚动文字数组 `scrollText`、是否启用单条文字滚动 `single`、滚动区域宽高 `width` 和 `height`、滚动区域和文字样式 `boardStyle` 和 `textStyle`、滚动条数 `amount`、间距 `gap`、动画间隔 `interval` 和 `step`、以及垂直滚动时间间隔 `verticalInterval`。组件内置多种样式调整功能,并提供在线预览示例。
Vue3文字滚动(TextScroll)
|
4月前
|
JavaScript 前端开发 UED
教你用vue自定义指令做一个组件的遮罩层loading效果
教你用vue自定义指令做一个组件的遮罩层loading效果
381 0
|
4月前
|
JavaScript
Vue-Awesome-Swiper基本能解决你所有的轮播需求(vue的问题)
Vue-Awesome-Swiper基本能解决你所有的轮播需求(vue的问题)
435 1