uniapp vue3-swiper(概念版)

uniapp vue3-swiper(概念版)

使用三个元素,实现无限轮播
概念就是:左滑结束第一个元素移动到最后;右滑结束最有一个元素移动到第一个。
*没有自动轮播功能哦,没写,因为我项目中没需要自动轮播功能,应该是加个定时器就可以了

使用示例

 <!-- 使用questionSwiper.jump(index,timing)跳转;  或者直接修改current属性,都可以 -->
<b-question-swiper :items="exercise.questions" ref="questionSwiper" :current="exercise.index" @change="onIndexChange">
  <template v-slot:default="{item,index}">
    <b-question :value="item" @selected="onSelectOption" :numberName="(index+1)+'/'+exercise.questions.length+''" :result="showResult"></b-question>
  </template>
</b-question-swiper>
       

就不花时间寒暄了,直接贴完整代码。

<template>
  <view @touchstart="_onTouchStart($event)" @touchmove="_onTouchMove($event)" @touchend="_onTouchEnd"
        class="custom-swiper">
    <template v-for="(item,index) in list" :key="index">
      <view class="item" v-if="item.item" v-show="item.show" :animation="item.animation">
        <slot :item="item.item" :index="item.index"></slot>
      </view>
    </template>
  </view>
</template>

<script lang="ts" setup>
import {computed, onMounted, ref, watch} from 'vue'

// 暂时不知道怎么更好的设置元素宽度,只能先满宽了
const windowWidth = uni.getSystemInfoSync().windowWidth;

interface Props {
  duration?: number;
  items: any[] | Array<any>;
  current?: number,
}

const emits = defineEmits<{ (e: 'change', index: number): void }>()
const props = withDefaults(defineProps<Props>(), {duration: 500, items: () => [], current: 0});
const duration = computed<number>(() => props.duration)

watch(() => props.current, (newValue, oldValue) => {
  let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
  if (newValue !== centerData.value.index) {
    jump(newValue)
  }
  console.log(newValue, centerData.value.index)
})

interface _ItemVO {
  item?: any;
  index: number;
  animation: any;
  translateX: number;
  show: boolean;
}

const leftItem = ref<_ItemVO>({index: -1, animation: {}, translateX: -windowWidth, show: true})
const centerItem = ref<_ItemVO>({index: 0, animation: {}, translateX: -windowWidth, show: true})
const rightItem = ref<_ItemVO>({index: 1, animation: {}, translateX: -windowWidth, show: true})

// 一直保持3条可循环数据,元素可以为空,为空时不执行slot展示
const list = computed<_ItemVO[]>(() => {
  let leftData = leftItem.value.translateX === -windowWidth ? leftItem : centerItem.value.translateX === -windowWidth ? centerItem : rightItem;
  let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
  let rightData = leftItem.value.translateX === windowWidth ? leftItem : centerItem.value.translateX === windowWidth ? centerItem : rightItem;

  if (leftData.value.index >= 0) {
    leftData.value.item = props.items[leftData.value.index]
  } else {
    leftData.value.item = null;
  }
  centerData.value.item = props.items[centerData.value.index];
  if (rightData.value.index <= props.items.length - 1) {
    rightData.value.item = props.items[rightData.value.index]
  } else {
    rightData.value.item = null;
  }
  return [leftItem.value, centerItem.value, rightItem.value];
});

// 左侧动画
const leftAnimation = uni.createAnimation({timingFunction: 'ease'})
const centerAnimation = uni.createAnimation({timingFunction: 'ease'})
const rightAnimation = uni.createAnimation({timingFunction: 'ease'})

// 是否移动中,移动中不能再移动,等待动画结束
const moving = ref<boolean>(false);

onMounted(() => {
  leftAnimation.translateX(-windowWidth).step({duration: duration.value});
  leftItem.value.animation = leftAnimation.export();
  leftItem.value.translateX = -windowWidth;
  leftItem.value.index = props.current - 1;

  centerAnimation.translateX(0).step({duration: duration.value});
  centerItem.value.animation = centerAnimation.export();
  centerItem.value.translateX = 0;
  centerItem.value.index = props.current;

  rightAnimation.translateX(windowWidth).step({duration: duration.value});
  rightItem.value.animation = rightAnimation.export();
  rightItem.value.translateX = windowWidth;
  rightItem.value.index = props.current + 1;
})

const touchData = ref<{ start: number, move: number }>({start: 0, move: 0});

function _onTouchStart(event: TouchEvent) {
  touchData.value.start = event.touches[0].clientX;
}

function _onTouchMove(event: TouchEvent) {
  if (moving.value) {
    return;
  }
  touchData.value.move = touchData.value.start - event.touches[0].clientX;

  leftAnimation.translateX(leftItem.value.translateX - touchData.value.move).step({duration: 0});
  leftItem.value.animation = leftAnimation.export();

  centerAnimation.translateX(centerItem.value.translateX - touchData.value.move).step({duration: 0});
  centerItem.value.animation = centerAnimation.export();

  rightAnimation.translateX(rightItem.value.translateX - touchData.value.move).step({duration: 0});
  rightItem.value.animation = rightAnimation.export();
}

/**
 *
 * @param event 兼容触摸控制
 * @param transitionTime 动画耗时
 * @param isLast 是否是最后一个动画,否则不执行缓慢滑动
 */
function _onTouchEnd(event: TouchEvent | null, transitionTime: number = duration.value, isLast: boolean = true): Promise<void> {
  if (touchData.value.move === 0 || moving.value) {
    return new Promise<void>((ok) => ok());
  }
  let leftData = leftItem.value.translateX === -windowWidth ? leftItem : centerItem.value.translateX === -windowWidth ? centerItem : rightItem;
  let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
  let rightData = leftItem.value.translateX === windowWidth ? leftItem : centerItem.value.translateX === windowWidth ? centerItem : rightItem;
  leftItem.value.show = true;
  centerItem.value.show = true;
  rightItem.value.show = true;
  let left = touchData.value.move > 0;
  let reset = Math.abs(touchData.value.move) <= 20;
  if (!left && centerData.value.index <= 0) {
    reset = true;
  }
  if (centerData.value.index >= props.items.length - 1 && left) {
    reset = true;
  }
  if (reset) {
    leftAnimation.translateX(leftItem.value.translateX).step({duration: 100});
    leftItem.value.animation = leftAnimation.export();

    centerAnimation.translateX(centerItem.value.translateX).step({duration: 100});
    centerItem.value.animation = centerAnimation.export();

    rightAnimation.translateX(rightItem.value.translateX).step({duration: 100});
    rightItem.value.animation = rightAnimation.export();
    return new Promise<void>((ok) => ok());
  }

  moving.value = true;
  let add = left ? -windowWidth : windowWidth;
  let leftTranslateX = leftData.value.translateX + add;
  let centerTranslateX = centerData.value.translateX + add;
  let rightTranslateX = rightData.value.translateX + add;
  let timingFunction: 'linear' | 'ease' | 'ease-in' | 'ease-in-out' | 'ease-out' | 'step-start' | 'step-end' = isLast ? 'ease' : 'linear';
  leftAnimation.translateX(leftTranslateX).step({duration: transitionTime, timingFunction: timingFunction});
  leftData.value.animation = leftAnimation.export();
  centerAnimation.translateX(centerTranslateX).step({duration: transitionTime, timingFunction: timingFunction});
  centerData.value.animation = centerAnimation.export();
  rightAnimation.translateX(rightTranslateX).step({duration: transitionTime, timingFunction: timingFunction});
  rightData.value.animation = rightAnimation.export();
  return new Promise<void>((ok) => {
    setTimeout(() => {
      // 动画执行完成
      centerData.value.translateX = centerTranslateX;
      if (left) {
        leftData.value.show = false;
        leftData.value.translateX = rightData.value.translateX;
        leftAnimation.translateX(leftData.value.translateX).step({duration: 0});
        leftData.value.animation = leftAnimation.export();
        leftData.value.show = true
        rightData.value.translateX = rightTranslateX;
      } else {
        rightData.value.show = false;
        rightData.value.translateX = leftData.value.translateX;
        rightAnimation.translateX(rightData.value.translateX).step({duration: 0});
        rightData.value.animation = rightAnimation.export();
        rightData.value.show = true
        leftData.value.translateX = leftTranslateX;
      }
      if (left) {
        leftData.value.index = rightData.value.index + 1;
      } else {
        rightData.value.index = leftData.value.index - 1;
      }
      touchData.value.move = 0;
      moving.value = false;
      // 让动画飞一会儿
      setTimeout(() => {
        _change()
        ok();
      }, 1)
    }, transitionTime - 2)
  })

}

function _change() {
  let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
  emits('change', centerData.value.index)
}

function jump(index: number, transitionTime: number = 500): Promise<void> {
  let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
  let time = transitionTime / Math.abs(index - centerData.value.index);
  return _jump(index, time)
}

function _jump(index: number, time: number): Promise<void> {
  return new Promise<void>((ok) => {
    let centerData = leftItem.value.translateX === 0 ? leftItem : centerItem.value.translateX === 0 ? centerItem : rightItem;
    if (index < 0 || index > props.items.length - 1 || index == centerData.value.index) {
      return ok();
    }
    let isLast = index === centerData.value.index - 1 || index == centerData.value.index + 1;
    if (centerData.value.index < index) {
      // 向后跳转
      touchData.value.move = 100;
      _onTouchEnd(null, time, isLast).then(() => {
        _jump(index, time).then(ok)
      })
    } else if (centerData.value.index > index) {
      touchData.value.move = -100;
      _onTouchEnd(null, time, isLast).then(() => {
        _jump(index, time).then(ok)
      })
    } else {
      return ok();
    }
  })

}

</script>

<style scoped>
.custom-swiper {
  position: relative;
}

.custom-swiper .item {
  position: absolute;
}
</style>

如果不使用uniapp的话,需要把代码中uni.*相关功能修改一下,但这可能比较麻烦。因为动画使用的uni.createAnimation函数

由于本人并不是前端专业,可能有一些Bug。如果有,请留言。但不一定会改哈哈

UniApp是一个基于Vue.js的跨平台开发框架,可以用于开发iOS、Android、H5等多个平台的应用程序。而vue-awesome-swiper是一个基于SwiperVue组件,用于实现轮播图等滑动效果。 要在UniApp使用vue-awesome-swiper,首先需要安装它。可以通过npm或者yarn进行安装,命令如下: ``` npm install vue-awesome-swiper --save ``` 安装完成后,在需要使用的页面中引入vue-awesome-swiper组件,并注册为全局组件或局部组件。例如,在`main.js`中全局注册: ```javascript import Vue from &#39;vue&#39; import VueAwesomeSwiper from &#39;vue-awesome-swiper&#39; import &#39;swiper/css/swiper.css&#39; Vue.use(VueAwesomeSwiper) ``` 然后,在需要使用轮播图的页面中,可以直接使用`<swiper>`标签来创建轮播图。例如: ```html <template> <swiper :options="swiperOptions"> <swiper-slide v-for="(item, index) in swiperList" :key="index"> <img :src="item.imgUrl" alt=""> </swiper-slide> <div class="swiper-pagination" slot="pagination"></div> </swiper> </template> <script> export default { data() { return { swiperOptions: { autoplay: true, loop: true, pagination: { el: &#39;.swiper-pagination&#39;, }, }, swiperList: [ { imgUrl: &#39;https://example.com/image1.jpg&#39; }, { imgUrl: &#39;https://example.com/image2.jpg&#39; }, { imgUrl: &#39;https://example.com/image3.jpg&#39; }, ], } }, } </script> ``` 在上面的代码中,`swiperOptions`是轮播图的配置项,可以根据需要进行设置。`swiperList`是轮播图的数据列表,可以根据实际情况进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值