Vue2走马灯(Carousel)

38 篇文章 5 订阅

Vue2走马灯扩展版(Carousel)

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

可自定义设置以下属性:

  • 轮播图片数组(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...'
            }
          ]
       }
    }
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值