UniApp 小程序实现自定义每张图片播放时长的轮播图(基于 uView 的 u-swiper)

前言

在 UniApp 小程序开发中,原生 swiper 组件或 uView 的 u-swiper 组件默认仅支持固定间隔 的自动轮播,无法满足 "不同轮播图项设置不同播放时长" 的业务需求(比如广告轮播中,重要广告展示更久,普通广告展示时间更短)。本文基于 uView 的u-swiper组件封装了一个可自定义每张图片播放时长的通用轮播图组件,解决了固定间隔轮播的局限性,同时兼顾了手动滑动交互和内存泄漏问题。

实现思路

  1. 基于 uView 的u-swiper搭建基础轮播结构,关闭原生 autoplay,改用自定义定时器控制自动切换;
  2. 通过props接收父组件传递的轮播数据(包含每张图的自定义播放时长playTime);
  3. 利用setTimeout定时器实现自动切换,每次切换时根据当前轮播项的playTime重新设置定时器时长;
  4. 监听手动滑动事件(touchstart/touchend),滑动时暂停自动播放,滑动结束后恢复,避免交互冲突;
  5. 监听轮播数据变化、组件销毁生命周期,做好定时器清理和边界处理(如数据为空的提示),防止内存泄漏。

完整组件代码

javascript 复制代码
<template>
  <div id="myswiper">
    <!-- uView轮播组件,关闭原生自动播放,手动控制current -->
    <u-swiper
      :list="banner"
      height="950"
      :indicator="false"
      :autoplay="false"
      :current="currentIndex"
      @change="handleSwiperChange"
      @touchstart="handleTouchStart"
      @touchend="handleTouchEnd"
    ></u-swiper>
  </div>
</template>

<script>
export default {
  name: "Myswiper",
  // 接收父组件传递的轮播数据
  props: {
    banner: {
      type: Array,
      default: () => [],
      required: true // 标记为必传,增强健壮性
    },
  },
  data() {
    return {
      currentIndex: 0, // 当前轮播图索引
      timer: null, // 定时器对象,用于控制自动切换
      isTouching: false, // 标记是否正在手动滑动,避免滑动时自动切换
    };
  },
  // 监听banner数据变化(如异步请求加载数据后)
  watch: {
    banner: {
      handler(newVal) {
        // 边界处理:轮播数据为空时给出提示
        if (newVal.length === 0) {
          console.error("【Myswiper组件】轮播图数据为空,请检查props传递!");
          return;
        }
        // 数据更新后重新启动自动播放
        this.startAutoPlay();
      },
      deep: true, // 深度监听数组内容变化(如数组项新增/修改/删除)
      immediate: true // 组件初始化时立即执行一次监听逻辑
    },
  },
  // 组件销毁前清理定时器,防止内存泄漏
  beforeDestroy() {
    this.clearTimer();
  },
  methods: {
    /**
     * 启动自动播放(核心方法)
     * 每次执行都会清除旧定时器,根据当前轮播项的playTime设置新定时器
     */
    startAutoPlay() {
      // 先清除旧定时器,避免多个定时器叠加导致切换速度异常
      this.clearTimer();
      
      // 获取当前轮播项的播放时长:优先取playTime(秒),默认3秒(3000毫秒)
      const currentPlayTime = this.banner[this.currentIndex].playTime || 3;
      const currentDuration = currentPlayTime * 1000;

      // 设置新定时器,到时间后切换下一张
      this.timer = setTimeout(() => {
        // 非手动滑动状态下才执行切换
        if (!this.isTouching) {
          this.nextSlide();
        }
      }, currentDuration);
    },

    /**
     * 切换到下一张轮播图
     * 实现循环切换:索引超出长度后回到0
     */
    nextSlide() {
      this.currentIndex = (this.currentIndex + 1) % this.banner.length;
      // 切换后重新启动自动播放(使用新轮播项的时长)
      this.startAutoPlay();
    },

    /**
     * 清除定时器
     * 通用方法,避免重复代码
     */
    clearTimer() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null; // 清空定时器对象,便于后续判断
      }
    },

    /**
     * 监听u-swiper切换事件(手动滑动触发)
     * @param {Number} e - 切换后的索引值
     */
    handleSwiperChange(e) {
      this.currentIndex = e;
      // 手动切换后重置自动播放(使用新索引对应的时长)
      this.startAutoPlay();
    },

    /**
     * 触摸开始:标记为滑动状态,暂停自动播放
     */
    handleTouchStart() {
      this.isTouching = true;
      this.clearTimer(); // 滑动时清除定时器,避免自动切换干扰手动操作
    },

    /**
     * 触摸结束:取消滑动状态,恢复自动播放
     */
    handleTouchEnd() {
      this.isTouching = false;
      this.startAutoPlay(); // 滑动结束后重新启动自动播放
    },
  },
};
</script>

<style scoped lang='scss'>
#myswiper {
  width: 100%;
  // 可根据需求自定义样式,避免样式穿透可使用/deep/
  /deep/ .u-swiper__item {
    img {
      width: 100%;
      height: 100%;
      object-fit: cover; // 保持图片比例,填充容器
    }
  }
}
</style>

代码核心解析

1. Props 与数据监听

  • banner:接收父组件传递的轮播数据,标记为required: true增强健壮性;
  • watch监听bannerdeep: true监听数组内容变化,immediate: true保证组件初始化时立即执行逻辑,解决 "异步加载数据后轮播不自动播放" 的问题。

2. 定时器核心逻辑

  • startAutoPlay:每次执行先清除旧定时器,再根据当前轮播项的playTime(秒转毫秒)设置新定时器;
  • nextSlide:通过取余运算(currentIndex + 1) % banner.length实现循环切换;
  • clearTimer:通用清理方法,避免定时器叠加导致的切换异常。

3. 手动滑动交互处理

  • touchstart:标记isTouching = true,清除定时器,防止滑动时自动切换;
  • touchend:标记isTouching = false,重启自动播放,恢复轮播逻辑;
  • handleSwiperChange:手动切换后更新索引,重启自动播放,保证切换后使用新项的时长。

4. 生命周期与边界处理

  • beforeDestroy:清理定时器,防止组件销毁后定时器仍执行导致内存泄漏;
  • 数据为空判断:给出明确的控制台提示,便于调试。

组件使用示例(父组件)

javascript 复制代码
<template>
  <view class="index-page">
    <!-- 引入自定义轮播图组件 -->
    <Myswiper :banner="bannerList" />
  </view>
</template>

<script>
// 引入组件
import Myswiper from "@/pages/index/component/Myswiper.vue";

export default {
  components: {
    Myswiper,
  },
  data() {
    return {
      // 轮播数据:playTime为每张图的播放时长(单位:秒)
      bannerList: [
        {
          image: "/static/banner1.png", // 图片地址
          playTime: 3, // 播放3秒
          url: "/pages/detail/detail1" // 可选:跳转链接
        },
        {
          image: "/static/banner2.png",
          playTime: 5, // 播放5秒
          url: "/pages/detail/detail2"
        },
        {
          image: "/static/banner3.png",
          playTime: 2, // 播放2秒
          url: "/pages/detail/detail3"
        }
      ],
    };
  },
  // 模拟异步加载轮播数据
  onLoad() {
    // 模拟接口请求
    setTimeout(() => {
      this.bannerList = [
        { image: "/static/banner4.png", playTime: 4, url: "/pages/detail/detail4" },
        { image: "/static/banner5.png", playTime: 6, url: "/pages/detail/detail5" },
      ];
    }, 1000);
  },
};
</script>
相关推荐
奚大野...2 小时前
uni-app手机端项目touchmove禁止页面上下拉滑动
前端·javascript·uni-app
内存不泄露4 小时前
二手物品交易平台
spring boot·小程序·django
酒醉的胡铁4 小时前
uniapp运行到鸿蒙证书配置
服务器·uni-app·harmonyos
华玥作者6 小时前
uni-app + Vite 项目中使用 @uni-helper/vite-plugin-uni-pages 实现自动路由配置(超详细)
前端·uni-app·vue·vue3·vite
狼性书生6 小时前
uniapp+vue3实现的简单吐司通知弹窗组件
前端·uni-app·vue·组件·插件
说私域7 小时前
基于AI智能名片链动2+1模式预约服务商城小程序的数据管理与系统集成研究
大数据·人工智能·小程序
Front思8 小时前
uniapp解决点击穿透问题总结
uni-app
说私域8 小时前
用户感知断裂与商业模式颠覆:AI智能名片链动2+1模式S2B2C商城小程序的破局之道
大数据·人工智能·小程序
peachSoda78 小时前
uniapp开发小程序 使用scroll-view时左右滑动切换无法回到最左边的bug解决方案
小程序·uni-app