vue3的版本也是出来了很久了,但由于实际的工作比较难用的上(现在还是用的vue2的开发模式),所以一直看vue3的学习也就慢慢忘的差不多,主要是用的少,捡起来经常写还是很快能上手的。这次真好有个公司的小项目练练手,自己负责随便用随便搞 :!(捂嘴笑) 新的技术还是要勇于尝试的,学习也是要不停止境的,这次的项目中有一个比较多的场景就是轮播图了,所以在写的时候打算是把轮播图抽离出来,在这之前也就是找找的轮子直接用,也没有仔细的研究去写出来,导致拿上来的时候一时间有点迷,在看了一些简单的示例之后呢,就仿了一个简单的,下面请看:

设计图


这是设计图给出的样子,弧形界面套壳加上里面的 png 图片组成一张图。

再说轮播图之前我们可以先回顾复习一下轮播图的简单实现,总结几点;

  1. 排版问题,单张的图片一定是全屏的宽度。
  2. 高度可以是自适应,最好做成等高。
  3. 如果是无缝轮播,可以拷贝一份图片插入到后面,保证循环。
  4. 注意定时器的使用和清除。
  5. 通过监听事件 touchend 来获取滚动的索引判断是左滑右滑状态。

找来的一张图表示一下无缝的感觉,但本次封装不做无缝处理,只是顺带了解下。

无缝轮播

了解以上的知识储备和无缝介绍,那我们就开始下一项的vue3代码实践。

组件设计采用vue3的 setup 函数,这样也是为了把属于轮播部分的代码抽离,以免页面代码量过多混淆视线。
看一下代码骨架结构:

<div
  class="swiper"
  ref="swiperRef"
  @touchstart="touchStart"
  @touchmove="touchMove"
  @touchend="touchEnd"
>
  <div class="item" v-for="(slider, index) in sliders" :key="index">
    <div class="con">
      <img :src="slider" alt="" />
    </div>
  </div>
</div>

<script>
  import { ref } from 'vue'
  import useSlider from './use-Slider'
  
  export default {
    data() {
      return {
        sliders: [ '1.png', '2.png', '3.png' ]
      }
    },
    setup() {
      const swiperRef = ref(null)
  
      const { touchStart, touchMove, touchEnd, currentIndex } = useSlider(swiperRef)
  
      return {
        swiperRef,
        touchStart,
        touchMove,
        touchEnd,
        currentIndex
      }
    },
  }
</script>

setup p函数中返回所需要的函数即可在页面上直接使用。
再看一下 use-slider.js 中的代码:

import { ref, onMounted, reactive } from 'vue'

export default function(swiperRef) {
  // 图片组的总宽度
  const imgWidth = ref(0)
  // 图片的数量
  const len = ref(0)
  // 当前的索引值
  const currentIndex = ref(0)
  // 当前是否是左右滑动的状态
  const isMove = ref(false)
  // 开始滑动的位置
  const startOffset = ref(0)
  // 开始滑动的坐标位置
  const startpoint = reactive({
    x: 0,
    y: 0
  })
  // 计算的距离
  const distance = reactive({
    x: 0,
    y: 0
  })
  
  // 初始化的时候执行轮播
  onMounted(() => {
    initSlider()
  })

  function initSlider() {
    // 获取宽度
    imgWidth.value = swiperRef.value.offsetWidth
    len.value = swiperRef.value.querySelectorAll('.item').length - 1
  }

  function touchStart(e) {
    const touch = e.changedTouches[0]
    startpoint.x = touch.pageX
    startpoint.y = touch.pageY

    const translatex = currentIndex.value * -imgWidth.value
    // 保存初次滑动的位置,以便第二次滑动的时候重置位置
    startOffset.value = translatex

    swiperRef.value.style.transition = 'none'
    swiperRef.value.style.transform = `translateX(${translatex}px)`
  }

  function touchMove(e) {
    const touch = e.changedTouches[0]
    distance.x = touch.pageX - startpoint.x
    distance.y = touch.pageY - startpoint.y
    
    // 判断手指是否是横行滑动
    if (Math.abs(distance.x) - Math.abs(distance.y) > 5) {
      isMove.value = true
      e.preventDefault()
    } else if(Math.abs(distance.x) - Math.abs(distance.y) < 5){
      isMove.value = false
    }
    if (isMove.value) {
      let translatex = startOffset.value + distance.x
      // 做个临界点回弹
      if (translatex > 0) {
        translatex = translatex > 30 ? 30 : translatex
      } else {
        const d = -(imgWidth.value * len.value + 30)
        translatex = translatex < d ? d : translatex
      }
      swiperRef.value.style.transform = `translateX(${ translatex }px)`
    }
  }

  function touchEnd() {
     if (Math.abs(distance.x) > imgWidth.value * proportion) {
      currentIndex.value -= distance.x / Math.abs(distance.x)
    }
    // 如果超出就重置
    if (currentIndex.value < 0) {
      currentIndex.value = 0
    } else if (currentIndex.value > len.value) {
      currentIndex.value = len.value
    }

    if (isMove.value) {
      let translatex = currentIndex.value * -imgWidth.value
      swiperRef.value.style.transition = 'all .3s'
      swiperRef.value.style.transform = `translateX(${ translatex }px)`
      isMove.value = false
    }
  }

  return {
    touchStart,
    touchMove,
    touchEnd,
    isMove,
    currentIndex
  }
}