更新记录

1.2.6(2025-02-08)

  • 修复ml-swiper-v3在app端的已知BUG

1.2.5(2025-01-07)

  • 新增 ml-swiper-app app特有组件,修复ml-swiper-v3 已知BUG(文档末尾提供示例代码)

1.2.4(2024-12-19)

  • 调整组件中的钩子函数mounted->created
查看更多

平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 4.0 app-vue app-nvue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari

组件版本说明

推荐购买源码版权,普通版权可能存在解析失败的问题

ml-swiper-v2、ml-swiper-v3 均支持 VUE2、VUE3

下文提供了 vue3 版本代码示例,末尾提供了 vue2 版本代码示例

特别说明:

  • ml-swiper-v2、ml-swiper-v3 后面的 -v{n} 仅表示 ml-swiper 组件的版本 并不代表 vue 的版本
  • ml-swiper-v2、ml-swiper-v3 后面的 -v{n} 仅表示 ml-swiper 组件的版本 并不代表 vue 的版本
  • ml-swiper-v2、ml-swiper-v3 后面的 -v{n} 仅表示 ml-swiper 组件的版本 并不代表 vue 的版本
  • ml-swiper-v2、ml-swiper-v3 均支持 VUE2、VUE3

特别说明

特别说明:

  • 收费代码有版权问题,且发布后的插件会被自动加密,如非购买本插件出现的各种问题,均与此有关。
  • 如正常购买了插件,出现了运行报错,或者案例代码运行报错,均可入群与作者联系,作者将提供帮助,并解决问题。
  • 该组件将持续维护,有任何问题均可留言。
  • 非购买出现的问题,非组件本身问题 作者将不予处理和解决,切勿恶意差评,创作不易 还请理解。
  • 购买普通版权后,想要源码的可插件市场直接购买源码,如需要继续扩展 可以直接联系作者 索要底层源代码 以供扩展使用。
  • 使用案例时特别注意,案例中的函数名会被转义为空,报错时 请先检查是否存在函数名为空的,为空时补全后重新运行即可。
  • 推荐使用使用 ml-swiper-v3 组件,底层已经封装完成,无需考虑数据量的问题,开箱即用。

功能概览

  • 支持视频双击点赞功能,单击暂停播放和播放功能
  • 支持配置 是否自动切换下一个视频,当前视频播放完成后 会自动切换到下一个视频
  • 支持 m3u8 流媒体视频资源,可以直接使用,无需特殊配置
  • 提供可自定义进度条插槽,可以实现自定义进度条功能,不想自定义可以使用默认进度条功能
  • 支持微信小程序、H5、和 APP
  • 提供了多种插槽,可以完全实现自定义各种组件
  • 进度条拖动功能
  • 视频、图片展示功能

以上只是简单几个小功能,更多具体功能请详细阅读文档

案例报错说明

缺少函数名

网页示例缺少函数名,可以直接使用readme.md中的示例代码

// @ondblclick 
function (e) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释 被转义掉的函数名为:
    console.log(e);
}
  • 使用案例时特别注意,案例中的函数名会被转义为空,报错时 请先检查是否存在函数名为空的,为空时补全后重新运行即可。

试用时报错

试用时报错:Failed to mount component: template or render function not defined. found in......

如果试用时组件运行报错 [Vue warn]: Failed to mount component: template or render function not defined. found in ...... 请注意,原因是试用组件 被加密了,导致无法被解析到,正常购买不会有问题,如有其他问题可在群里提出,作者看到后会帮助解决处理。

初始值为空

初始值为空:[system] TypeError: Cannot read properties of undefined (reading 'includes')at ......

如果购买了插件,运行时报错:[system] TypeError: Cannot read properties of undefined (reading 'includes')at ......,原因是datas[0]?.includes初始值为空,导致的报错,请修改示例代码

<!-- 点赞:修改前 -->
<uni-icons type="heart-filled" size="35" :color="datas[0]?.includes(item.videoId) ? '#ff0004' : '#fff'" @tap="iconClick(0, item.videoId)" />
<!-- 修改后 -->
<uni-icons type="heart-filled" size="35" color="#fff" />

<!-- 收藏:修改前 -->
<uni-icons type="star-filled" size="35" :color="datas[1]?.includes(item.videoId) ? '#ff0' : '#fff'" @tap="iconClick(1, item.videoId)" />
<!-- 修改后 -->
<uni-icons type="star-filled" size="35" color="#fff" />

组件支持情况

ml-swiper-v3版本:支持 VUE2、VUE3

H5:支持 m3u8 流媒体资源




组件其他版本

更多组件 请前往 作者主页查看 :https://ext.dcloud.net.cn/publisher?id=1784252

ml-swiper :该版本的插件 为 ml-swiper 的第一版 插件 适用于 案例测试、组件学习、抖音功能分析等;该版本的插件 仅支持 VUE3;


ml-swiper-v2 :该 插件 是在 ml-swiper 版本上做的升级,支持 VUE2、VUE3,并且支持了 m3u8 流媒体资源;此版本的插件 适用于 个人或者公司 的二次开发 定制专属插件;插件内 功能、方法 均有详细注释说明,并且 组件内详细说明了 抖音APP 的实现逻辑,以及抖音官方的说明;(通过该 版本的组件 可以实现一个 适用于自己或者公司 专属的 扩展组件)


ml-swiper-v3 :该 插件 是在 ml-swiper-v2 的基础上 做出的最终版,并且优化了 大资源数据下 导致的上下滑动卡顿的问题。该组件不需要特殊配置,容易上手,内置插槽 便于实现个性化UI页面,并且提供了 很多组件方法 便于自定义实现更多的业务逻辑;


ml-swiper-x:该 插件是 uni-app-x 版,性能远远高于 uni-app 流畅度和体验度 大大提升,让短视频刷起来更加流程丝滑(注:这得益于uni-app-x打包成原生代码,大大提升了性能)



新特性概览

进度条拖动img视频图片组合img自动切换 img


真实运行示例


APP-雷电模拟器



APP端代码示例




手机端-微信小程序





手机端-QQ浏览器





小程序、H5网页端代码示例



拖动进度条"

<ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" :customProgress="false" >
     <!-- ...... 省略重复代码 -->
</ml-swiper-v3>


自动切换下一个:autoChange="true"

<ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" :autoChange="true" >
    <!-- ...... 省略重复代码 -->
</ml-swiper-v3>

视频图片结合:imgList

<ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" :autoChange="true" >
    <!-- ...... 省略重复代码 -->
</ml-swiper-v3>
<script setup>
 import { ref } from 'vue';
 const lists = ref([
      {
        videoId: lists.value.length + 1,
        title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
        // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
        url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
        uploadTime: "2023-11-08 19:41",
        ipLocation: "上海",
        author: {
          authorId: 101,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "陌路",
          genderName: "男"
        }
      },
      {
        videoId: lists.value.length + 1,
        title: `图片列表测试。`,
        url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
        uploadTime: "2023-11-08 19:41",
        ipLocation: "上海",
        author: {
          authorId: 101,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "陌路",
          genderName: "男"
        },
        imgList: [
          'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
          'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
          'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
          'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
          'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
          'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
        ]
      }
      // ...... 省略重复代码
    ]);
</script>


组件参数props

属性名 类型 默认值 说明 必须
list Array [] 视频数据,默认空数组,参数详情下见options介绍,数据资源条数最少不得少于 3
width String 100% 组件宽度,默认与设备同宽
height String 100% 组件高度,默认与设备同高
criticalVal Number 2 临界值,当 list.length - currentIndex >= criticalVal时触发加载更多事件
progress Boolean true 是否显示进度条,默认显示进度条
duration Boolean true 是否显示播放时间,默认显示播放时长
customProgress Boolean false 自定义进度条
autoChange Boolean false 是否自动切换下一个视频
slideDuration Number 500 滑动动画时长,默认 500 与uniapp官方保持一致
showFullscreen String none 可选值:always | auto | none,APP端设置 auto 时和 always 一样,将一直显示,H5端设置 auto 时,将动态计算是否需要显示 全屏观看 按钮
customFullscreen Boolean false 是否使用自定义全屏观看按钮插槽,需要将 showFullscreen置为"always",并且customFullscreen为 true 可以配合 插槽 "fullscreen" 一起使用
startIndex Number 0 开始索引,从第 startIndex 个资源开始,指定开始索引,并且startIndex < list.length
initTipText String 初始化中... 初始化提示文本,默认 初始化中...
customInitTip Boolean false 自定义初始化文本插槽
showPlayBtn Boolean false 是否显示 播放按钮
slidingDistance Number 50 滑动距离 临界值,当无法触发swiper时将使用此值


视频数据详情options【数据资源条数最少不得少于 3 条】

属性名 类型 默认值 说明 必须
url String "" 视频资源地址
title String "" 视频标题
poster String "" 视频封面,支持JPG、PNG等常见图片文件
imgList Array<String> [] 用于展示图片资源
----- ----- 其他属性可根据需要自定义 ----- -----
比如: userInfo = {} 用户相关数据
比如: commentList = [] 评论数据列表
比如: date 视频发布时间
比如: location IP属地信息
。。。 。。。 。。。 。。。 。。。

组件事件Events

事件 参数 解释 说明
onchange (event)
滑动事件
event = {index, context, video} index:当前视频的索引
context:video的上下文对象
video:正在播放的视频数据
当视频上下滑动时,触发 onchange事件
onplay (event)
播放事件
event = {index, context, video, playing} index:当前视频的索引
context:video的上下文对象
video:正在播放的视频数据
playing:当前播放状态
当前视频播放时,触发 onplay事件
onpause (event)
暂停事件
event = {index, context, video, playing} index:当前视频的索引
context:video的上下文对象
video:正在播放的视频数据
playing:当前播放状态
当前视频暂停时,触发 onpause事件
onended (event)
结束事件
event = {index, context, video} index:当前视频的索引
context:video的上下文对象
当前视频播放结束时,触发 onpause事件
ontimeupdate (event)
进度变更事件
event事件 event:APP、小程序、H5 原生事件 视频进度条变化时触发ontimeupdate事件,可用来自定义进度条
onwaiting (event)
出现缓冲事件
event = {index, context, video} index:当前视频的索引
context:video的上下文对象
当前视频播放出现缓冲时,触发 onwaiting事件
onerror (event)
播放出错事件
event = {index, context, video, error, event} index:当前视频的索引
context:video的上下文对象
当前视频播放出错时,触发 onerror事件
onclick (event)
视频单击事件
event = {index, context, video, playing} index:当前视频的索引
context:video的上下文对象
video:当前视频的数据信息
playing:是否正在播放
当单击视频时触发onclick事件
ondblclick (event)
视频双击事件
event = {index, context, video, playing} index:当前视频的索引
context:video的上下文对象
video:当前视频的数据信息
当双击视频时触发ondblclick事件
loadmore(event)
加载更多数据
event = { index } index:当前视频的索引 list.length - currentIndex >= criticalVal时触发加载更多事件
fullscreenclick(event)
全屏时的点击事件
event = { screenX, screenY, screenWidth, screenHeight } 全屏时的点击坐标和全屏时的屏幕宽度和高度 仅全屏时点击才会触发当前事件
fullscreenchange(event)
当视频进入和退出全屏事件
event = {fullScreen, direction} direction取为 vertical 或 horizontal 当进入全屏和退出全屏时触发fullscreenchange事件,需要判断当前是否全屏时,可以使用此事件
longTap(event)
视频组件长按事件
event = {index, context, video, playing} index:当前视频的索引
context:video的上下文对象
video:当前视频的数据信息
playing:是否正在播放
当长按视频组件时会触发longTap(event)事件

组件插槽slot

插槽name 插槽参数 使用场景
default item:当前视频资源数据
index:当前视频索引
视频上方自定义播放按钮或者其他功能
right item:当前视频资源数据
index:当前视频索引
可以实现抖音右侧工具条:点赞、收藏、评论、设置等
bottom item:当前视频资源数据
index:当前视频索引
可以实现抖音作者名称,视频标题等
progress current:当前时间(s)
duration:总时长(s)
percentage:进度比
可以配合ontimeupdate拿到视频时长和当前时长 实现自定义进度条,需要progresscustomProgress 都为 true
fullscreen item:当前视频资源数据
index:当前视频索引
自定义全屏按钮插槽
initText - 自定义初始化文本插槽

插槽示例代码

default默认插槽

      <template v-slot:default="{ index }">
        <view class="body">
          <text class="text">{{ index + 1 }} / {{ lists.length }}</text>
        </view>
      </template>

right右侧工具条

      <template v-slot:right="{ item }">
        <view class="right">
          <image class="userAvatar" :src="item?.author?.avatar" @click="avatarClick"></image>
          <!-- 喜欢 -->
          <view class="icon">
            <uni-icons type="heart-filled" size="35" color="#fff" />
            <text class="icon-val">666</text>
          </view>
          <!-- 评论 -->
          <view class="icon">
            <uni-icons type="chat-filled" size="35" color="#fff" />
            <text class="icon-val">668</text>
          </view>
          <!-- 收藏 -->
          <view class="icon">
            <uni-icons type="star-filled" size="35" color="#fff" />
            <text class="icon-val">888</text>
          </view>
          <!-- 转发 -->
          <view class="icon">
            <uni-icons type="redo-filled" size="35" color="#fff" />
            <text class="icon-val">999</text>
          </view>
        </view>
      </template>

bottom底部视频标题

      <template v-slot:bottom="{ item }">
        <view class="bottom">
          <text class="title">{{ item?.title }}</text>
        </view>
      </template>

progress底部自定义进度条,需要customProgressprogress 同时设置为 true

      <template v-slot:progress="{ current, duration, percentage }">
       <view v-if="percentage" class="progress-box">
          <text>{{ current }}</text> 
          <progress class="progress" :percent="percentage" font-size="13"
            stroke-width="2" activeColor="#3ea5ff" backgroundColor="#EBEBEB" 
            active active-mode="forwards" />
          <text>{{ duration }}</text>
       </view>
      </template>

组件方法methods

事件 参数 说明
fullScreen() 进入全屏 - 全屏播放
exitFullScreen() 退出全屏 - 退出全屏播放
setRate(rate) 设置倍速 Number: 0.5 | 0.8 | 1.0 | 1.25 | 1.5 | 2.0 当前视频倍速,不同平台支持不一样
setSeek(val) 指定播放时间 Number: 60 指定播放时间,单位秒,从 60秒处 播放


方法使用示例

<!-- ...省略... -->
<ml-swiper-v3 ref="mlSwiperRef"></ml-swiper-v3>
<!-- ...省略... -->
<script setup>
  import { ref } from 'vue';
  const mlSwiperRef = ref(null);

  // ...省略...
  /**
   * @fullScreen 全屏
   */
  function fullScreen() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    mlSwiperRef.value?.fullScreen();
    // context?.requestFullScreen();
  }

  /**
   * @exitFullScreen 退出全屏
   */
  function exitFullScreen() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    mlSwiperRef.value?.exitFullScreen();
    // context?.exitFullScreen();
  }

  /**
   * @setRate 倍速
   * @param {Number} rate 0.5 | 0.8 | 1.0 | 1.25 | 1.5 | 2.0
   */
  function setRate(rate) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    mlSwiperRef.value?.setRate(rate);
    // context?.playbackRate(rate);
  }

  /**
   * @setSeek 跳转到指定位置播放
   * @param {Number} val 单位秒
   */
  function setSeek(val) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    mlSwiperRef.value?.setSeek(val);
    // context?.seek(val);
  }
  /// ...省略...
</script>

vue3 版本示例代码

组件示例代码

微信小程序 和 H5 网页端示例代码(注:H5端 需要安装 hls,详见下文: H5端使用说明

# H5 端 安装插件
npm install hls.js

示例代码中用到了 uni-icons 请自行安装

index.vue

<template>
    <!-- 这里使用 v-if 是为了防止 请求后台时还没返回数据 导致组件初始化时没有视频资源 而出现错误 -->
    <ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" @loadmore="loadmore" @ondblclick="" @onplay="onplay">
      <!-- 自定义内容,这里设置100%后 层级高于video,会导致单击、双击功能失效 -->
      <template v-slot:default="{ index }">
        <view class="body">
          <text class="text">{{ index + 1 }} / {{ lists.length }}</text>
        </view>
      </template>
      <!-- 右侧 -->
      <template v-slot:right="{ item }">
        <view class="right">
          <image class="userAvatar" :src="item?.author?.avatar" @click="avatarClick"></image>
          <!-- 喜欢 -->
          <view class="icon">
            <uni-icons type="heart-filled" size="35" :color="datas[0]?.includes(item.videoId) ? '#ff0004' : '#fff'" @tap="iconClick(0, item.videoId)" />
            <text class="icon-val">666</text>
          </view>
          <!-- 评论 -->
          <view class="icon">
            <uni-icons type="chat-filled" size="35" color="#fff" @tap="comment(item.videoId)" />
            <text class="icon-val">668</text>
          </view>
          <!-- 收藏 -->
          <view class="icon">
            <uni-icons type="star-filled" size="35" :color="datas[1]?.includes(item.videoId) ? '#ff0' : '#fff'" @tap="iconClick(1, item.videoId)" />
            <text class="icon-val">888</text>
          </view>
          <!-- 转发 -->
          <view class="icon">
            <uni-icons type="redo-filled" size="35" color="#fff" @tap="forward(item.videoId)" />
            <text class="icon-val">999</text>
          </view>
        </view>
      </template>
      <!-- 底部 -->
      <template v-slot:bottom="{ item }">
        <view class="bottom">
          <text class="title">{{ item?.title }}</text>
        </view>
      </template>
    </ml-swiper-v3>
</template>

<script setup>
  import { onMounted, reactive, ref } from 'vue';
  import { onHide } from "@dcloudio/uni-app";

  const lists = ref([]);
  const type = ref("v3"); // 当前 ml-swiper版本(当前版本是 ml-swiper-v3)
  const datas = reactive({0:[],1:[]});

  let count = 0, context = null;

  // @onplay 视频播放
  function onplay(event){
    context = event?.context;
    console.log("onplay:", event?.playing);
  }

  // @avatarClick 点击头像
  function avatarClick() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "点击头像", icon: "none", duration: 1000 });
  }

  // @comment 查看评论
  function comment(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "查看评论", icon: "none", duration: 1000 });
  }

  // @forward 转发
  function forward(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "转发", icon: "none", duration: 1000 });
  }

  // @iconClick 图标点击事件
  function iconClick(type, id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    datas[type] = (datas[type] || []);
    let index = datas[type]?.indexOf(id);
    if (index >= 0) {
      datas[type].splice(index, 1);
    } else {
      datas[type].push(id);
    }
  }
  //  @ondblclick 屏幕双击事件
  function ({ video }) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    console.log(video?.videoId);
    iconClick(0, video?.videoId);
  }

  // @loadmore  加载更多
  function loadmore() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "加载更多", icon: "none", duration: 1000 });
    for (var i = 0; i < 2; i++) {
      getList().forEach((item) => {
        count = count + 1;
        item.title = `【${count}】` + item.title;
        lists.value.push(item);
      });
    }
  }

  // 页面隐藏时,暂停播放
  onHide(() => {
    if (context) { context?.pause(); }
  });

  onMounted(() => {
    // 这里直接生成 200 条视频数据进行测试(模拟请求后台获取数据)
    uni.showToast({ title: "加载中...", icon: "loading", duration: 1500 });
    setTimeout(() => {
      let size = type.value == 'v2' ? 2 : 50;
      for (var i = 0; i < size; i++) {
        getList().forEach((item) => {
          count = count + 1;
          item.title = `【${count}】` + item.title;
          lists.value.push(item);
        });
      }
      uni.hideToast();
    }, 1500);
  });

  const getList = () => {
    return [
      {
        videoId: lists.value.length + 1,
        title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
        // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
        url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
        uploadTime: "2023-11-08 19:41",
        ipLocation: "上海",
        author: {
          authorId: 101,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "陌路",
          genderName: "男"
        }
      },
      {
        videoId: lists.value.length + 2,
        title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
        uploadTime: "2023-10-02 09:41",
        ipLocation: "贵州",
        author: {
          authorId: 102,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "御姐呀",
          genderName: "女"
        },
        imgList: [
          'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
          'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
          'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
          'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
          'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
          'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
        ]
      },
      {
        videoId: lists.value.length + 3,
        title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
        url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
        uploadTime: "2023-08-23 00:41",
        ipLocation: "广州",
        author: {
          authorId: 103,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "野花猫",
          genderName: "女"
        }
      },
      {
        videoId: lists.value.length + 4,
        title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
        url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
        uploadTime: "2023-07-02 14:41",
        ipLocation: "山西",
        author: {
          authorId: 104,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "蓝姬",
          genderName: "女"
        },
        imgList: [
          'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
          'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
          'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
          'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
          'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
          'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
        ]
      }
    ];
  }
</script>

<style scoped lang="scss">
  .body {
    position: absolute;
    top: 0;
    margin: 0 auto;
  }

  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0 auto;
  }

  .icon {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
    margin: 8px auto;
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }

  .progress {
    /* #ifndef APP-NVUE */
    width: 80%;
    /* #endif */
    height: 2px;
  }

  .progress-box {
    /* #ifndef APP-NVUE */
    width: 100%;
    display: flex;
    color: #fff;
    /* #endif */
    align-items: center;
    justify-content: space-around;
  }

  .bottom {
    margin: 10px;
  }

  .title {
    color: #fff;
    font-size: 13px;
  }
</style>



H5端使用说明

如果需要编译为 H5 端,则需要安装插件 hls,APP端 和 微信小程序 不需要安装

npm install hls.js

测试说明:H5 端测试了 QQ浏览器 支持良好,夸克浏览器 不支持该插件(夸克浏览器只支持内置播放器且悬浮于顶层)。 其他浏览器均为测试,如需使用 请自行测试。



APP代码示例

app端

APP端开发,暂不支持 在 vue 页面中使用 ml-swiper-v3 组件,如需开发 APP 应用,请将 vue 页面 改为 nvue 页面,其他不变即可正常使用该组件。

APP端提供了 ml-swiper-v3ml-swiper-app 两个组件,请根据实际情况进行选择

示例代码中用到了 uni-icons 请自行安装

index.nvue 下面是 ml-swiper-v3 示例代码

<template>
  <!-- 这里使用 v-if 是为了防止 请求后台时还没返回数据 导致组件初始化时没有视频资源 而出现错误 -->
  <ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" @loadmore="loadmore" @ondblclick="" @onplay="onplay">
    <!-- 右侧 -->
    <template v-slot:right="{ item }">
      <view class="right">
        <image class="userAvatar" :src="item?.author?.avatar" @click="avatarClick"></image>
        <!-- 喜欢 -->
        <view class="icon">
          <uni-icons type="heart-filled" size="35" :color="datas[0]?.includes(item.videoId) ? '#ff0004' : '#fff'" @tap="iconClick(0, item.videoId)" />
          <text class="icon-val">666</text>
        </view>
        <!-- 评论 -->
        <view class="icon">
          <uni-icons type="chat-filled" size="35" color="#fff" @tap="comment(item.videoId)" />
          <text class="icon-val">668</text>
        </view>
        <!-- 收藏 -->
        <view class="icon">
          <uni-icons type="star-filled" size="35" :color="datas[1]?.includes(item.videoId) ? '#ff0' : '#fff'" @tap="iconClick(1, item.videoId)" />
          <text class="icon-val">888</text>
        </view>
        <!-- 转发 -->
        <view class="icon">
          <uni-icons type="redo-filled" size="35" color="#fff" @tap="forward(item.videoId)" />
          <text class="icon-val">999</text>
        </view>
      </view>
    </template>
    <!-- 底部 -->
    <template v-slot:bottom="{ item }">
      <view class="bottom">
        <text class="title">{{ item?.title }}</text>
      </view>
    </template>
  </ml-swiper-v3>
</template>
<script setup >
  import { onMounted, reactive, ref } from 'vue';
  import { onHide } from "@dcloudio/uni-app";

  const lists = ref([]);
  const type = ref("v3");
  const datas = reactive({0:[],1:[]});

  let count = 0, context = null;
  // @onplay 视频播放
  function onplay(event){
    context = event?.context;
    console.log("onplay:", event?.playing);
  }
  // @avatarClick  点击头像
  function avatarClick() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "点击头像", icon: "none", duration: 1000 });
  }
  // @comment 查看评论
  function comment(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "查看评论", icon: "none", duration: 1000 });
  }
  //  @forward  转发
  function forward(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "转发", icon: "none", duration: 1000 });
  }
  // @iconClick 图标点击事件
  function iconClick(type, id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    datas[type] = (datas[type] || []);
    let index = datas[type]?.indexOf(id);
    if (index >= 0) {
      datas[type].splice(index, 1);
    } else {
      datas[type].push(id);
    }
  }
  // @ondblclick 双击事件
  function ({ video }) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    console.log(video?.videoId);
    iconClick(0, video?.videoId);
  }
  // @loadmore 加载更多
  function loadmore() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
    uni.showToast({ title: "加载更多", icon: "none", duration: 1000 });
    for (var i = 0; i < 2; i++) {
      getList().forEach((item) => {
        count = count + 1;
        item.title = `【${count}】` + item.title;
        lists.value.push(item);
      });
    }
  }
  // 页面隐藏时 暂停播放
  onHide(() => {
    if (context) { context?.pause(); }
  });

  onMounted(() => {
    // 这里直接生成 200 条视频数据进行测试(模拟请求后台获取数据)
    uni.showToast({ title: "加载中...", icon: "loading", duration: 1500 });
    setTimeout(() => {
      let size = type.value == 'v2' ? 2 : 50;
      for (var i = 0; i < size; i++) {
        getList().forEach((item) => {
          count = count + 1;
          item.title = `【${count}】` + item.title;
          lists.value.push(item);
        });
      }
      uni.hideToast();
    }, 1500);
  });

  const getList = () => {
    return [
      {
        videoId: lists.value.length + 1,
        title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
        // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
        url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
        uploadTime: "2023-11-08 19:41",
        ipLocation: "上海",
        author: {
          authorId: 101,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "陌路",
          genderName: "男"
        }
      },
      {
        videoId: lists.value.length + 2,
        title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
        uploadTime: "2023-10-02 09:41",
        ipLocation: "贵州",
        author: {
          authorId: 102,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "御姐呀",
          genderName: "女"
        },
        imgList: [
          'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
          'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
          'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
          'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
          'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
          'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
        ]
      },
      {
        videoId: lists.value.length + 3,
        title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
        // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
        url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
        uploadTime: "2023-08-23 00:41",
        ipLocation: "广州",
        author: {
          authorId: 103,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "野花猫",
          genderName: "女"
        }
      },
      {
        videoId: lists.value.length + 4,
        title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
        uploadTime: "2023-07-02 14:41",
        ipLocation: "山西",
        author: {
          authorId: 104,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "蓝姬",
          genderName: "女"
        },
        imgList: [
          'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
          'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
          'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
          'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
          'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
          'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
        ]
      }
    ];
  }
</script>
<style scoped lang="scss">
  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100;
  }

  .icon {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
    margin: 8px 0;
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .progress {
    /* #ifndef APP-NVUE */
    width: 80%;
    /* #endif */
    height: 2px;
  }

  .progress-box {
    /* #ifndef APP-NVUE */
    width: 100%;
    display: flex;
    color: #fff;
    /* #endif */
    align-items: center;
    justify-content: space-around;
  }

  .bottom {
    margin: 10px;
  }

  .title {
    color: #fff;
    font-size: 13px;
  }
</style>



下面是 ml-swiper-app 示例代码

<template>
  <view :style="`width:${width}px;height:${height}px;overflow:hidden;`">
    <ml-swiper-app v-if="list.length > 2" :list="list" :width="width" :height="height" :custom="custom"
      :progress="progress" :criticalVal="criticalVal" :autoplay="autoplay" :startIndex="startIndex"
      :autoChange="autoChange" @change="onchange" @toupper="toupper" @tolower="tolower" @loadmore="loadmore"
      @ondblclick="dblclick" @onplay="onplay" @onpause="onpause" @onended="" @onerror="onerror"
      @onwaiting="" @ontimeupdate="" @fullscreenchange="fullscreenchange"
      @fullscreenclick="fullscreenclick">

      <!-- custom="true":自定义 video 组件  -->
      <template v-if="custom" v-slot:default="{item}">
        <video :src="item.url" :title="item.title" :poster="item.poster" :autoplay="false" :controls="false"
          :loop="true" :show-center-play-btn='true' play-btn-position="center" :show-loading="false"
          :enable-progress-gesture="false" object-fit="contain" :style="`width:${width}px;height:${height}px;`"></video>
      </template>

      <!-- custom="false":使用默认 video 组件  -->
      <!-- 默认插槽:default = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:default="{ index }">
        <view> <text style="color: #fff;">{{ index + 1 }} / {{ list.length }}</text> </view>
      </template>
      <!-- 右侧插槽:right = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:right="{ item }">
        <view class="right">
          <image class="userAvatar" :src="item?.author?.avatar"></image>
          <!-- 喜欢 -->
          <view class="icon">
            <uni-icons type="heart-filled" size="35" color="#ff0004" />
            <text class="icon-val">666</text>
          </view>
          <!-- 评论 -->
          <view class="icon">
            <uni-icons type="chat-filled" size="35" color="#fff" />
            <text class="icon-val">668</text>
          </view>
          <!-- 收藏 -->
          <view class="icon">
            <uni-icons type="star-filled" size="35" color="#ff0" />
            <text class="icon-val">888</text>
          </view>
          <!-- 转发 -->
          <view class="icon">
            <uni-icons type="redo-filled" size="35" color="#fff" />
            <text class="icon-val">999</text>
          </view>
        </view>
      </template>
      <!-- 底部插槽:bottom = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:bottom="{ item }">
        <view :style="`width:${width}px;`">
          <text style="color: #fff;">{{ item.title }}</text>
        </view>
      </template>

      <!-- progress="false":不使用默认进度条,自定义进度条,需要配合 @ontimeupdate 事件获取当前播放时间 -->

    </ml-swiper-app>
  </view>
</template>

<script>
  import mlSwiperApp from '../../uni_modules/ml-swiper-v3/components/ml-swiper-v3/ml-swiper-app.vue';
  export default {
    components: {
      mlSwiperApp
    },
    data() {
      return {
        list: [], // 视频数据列表:{url:"视频资源地址", poster:"预览图,尽量给出,否则上下滑动时会黑屏", title:"视频标题"}
        custom: false, // 是否自定义 video 组件, 默认false
        progress: true, // 是否显示进度条,默认 true
        autoChange: true, // 是否自动播放下一个
        criticalVal: 2, // 临界值,默认 2 
        autoplay: true, // 初次加载时视频是否自动播放
        startIndex: 0, // 开始索引,从第 n 个资源开始,默认0
        width: uni.getSystemInfoSync().windowWidth, // 组件宽度
        height: uni.getSystemInfoSync().windowHeight // 组件高低
      }
    },
    created() {
      this.list = this.getList();
    },
    mounted() {},
    onHide() { // 如果需要离开页面暂停播放,尽量提供 onHide 
      this.pause();
    },
    onUnload() {
      this.pause();
      this.list = [];
      this.context = null;
    },
    methods: {
      onchange(e) {
        console.log(" === onchange 滑动 ===> ", e);
        console.log("\r\n");
      },
      toupper(e) {
        console.log(" === toupper 到顶了 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "到顶了",
          icon: 'none'
        });
      },
      tolower(e) {
        console.log(" === tolower 到底了 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "到底了",
          icon: 'none'
        });
      },
      loadmore(e) {
        console.log(" === loadmore 加载更多 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "加载更多",
          icon: 'none'
        });
        this.list = [...this.list, ...this.getList()];
      },
      dblclick(e) {
        console.log(" === dblclick 双击 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "双击",
          icon: 'none'
        });
      },
      onplay(e) {
        this.context = e.context;
        console.log(" === onplay 开始播放 ===> ", e);
        console.log("\r\n");
      },
      onpause(e) {
        console.log(" === onpause 暂停播放 ===> ", e);
        console.log("\r\n");
      },
      (e) {
        console.log(" ===  播放结束 ===> ", e);
        console.log("\r\n");
      },
      onerror(e) {
        console.error(e);
      },
      (e) {
        console.log(" ===  出现缓冲 ===> ", e);
        console.log("\r\n");
      },
      (e) {
        console.log(" ===  更新时长 ===> ", e);
      },
      fullscreenchange(e) {
        console.log(" ===  全屏变更 ===> ", e);
        console.log("\r\n");
      },
      fullscreenclick(e) {
        console.log(" ===  全屏点击 ===> ", e);
        console.log("\r\n");
      },
      pause() {
        if (this.context?.pause) {
          this.context.pause();
        }
      },
      play() {
        if (this.context?.play) {
          this.context.play();
        }
      },
      getList() {
        return [{
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
            poster: "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028",
              nickName: "陌路",
              genderName: "男"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `图片列表测试。`,
            poster: "http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028",
              nickName: "陌路",
              genderName: "男"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
            poster: "http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4",
            uploadTime: "2023-10-02 09:41",
            ipLocation: "贵州",
            author: {
              authorId: 102,
              avatar: "http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028",
              nickName: "御姐呀",
              genderName: "女"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `图片列表测试。`,
            poster: "http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028",
              nickName: "陌路",
              genderName: "男"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
            poster: "http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028",
            url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
            uploadTime: "2023-08-23 00:41",
            ipLocation: "广州",
            author: {
              authorId: 103,
              avatar: "http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028",
              nickName: "野花猫",
              genderName: "女"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
            poster: "http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-07-02 14:41",
            ipLocation: "山西",
            author: {
              authorId: 104,
              avatar: "http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028",
              nickName: "蓝姬",
              genderName: "女"
            }
          }
        ];
      }
    }
  }
</script>

<style scoped lang="scss">
  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0;
  }

  .icon {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }
</style>

vue2 版本示例代码

vue代码示例

示例代码中用到了 uni-icons 请自行安装

<template>
  <view class="wh-full">
      <!-- 这里使用 v-if 是为了防止 请求后台时还没返回数据 导致组件初始化时没有视频资源 而出现错误 -->
      <ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" @loadmore="loadmore" @ondblclick="" @onplay="onplay">
        <!-- 自定义内容,这里设置100%后 层级高于video,会导致单击、双击功能失效 -->
        <template v-slot:default="{ index }">
          <view class="body">
            <text class="text">{{ index + 1 }} / {{ lists.length }}</text>
          </view>
        </template>
        <!-- 右侧 -->
        <template v-slot:right="{ item }">
          <view class="right" v-show="item != null">
            <!-- 用户头像 -->
            <image v-show="item.author != null" class="userAvatar" :src="item.author.avatar" @click="avatarClick">
            </image>
            <!-- 点赞 -->
            <view class="icon">
              <uni-icons type="heart-filled" size="35" :color="datas[0].includes(item.videoId) ? '#ff0004' : '#fff'"
                @tap="iconClick(0, item.videoId)" />
              <text class="icon-val">666</text>
            </view>
            <!-- 评论 -->
            <view class="icon">
              <uni-icons type="chat-filled" size="35" color="#fff" @tap="comment(item.videoId)" />
              <text class="icon-val">668</text>
            </view>
            <!-- 收藏 -->
            <view class="icon">
              <uni-icons type="star-filled" size="35" :color="datas[1].includes(item.videoId) ? '#ff0' : '#fff'"
                @tap="iconClick(1, item.videoId)" />
              <text class="icon-val">888</text>
            </view>
            <!-- 转发 -->
            <view class="icon">
              <uni-icons type="redo-filled" size="35" color="#fff" @tap="forward(item.videoId)" />
              <text class="icon-val">999</text>
            </view>
          </view>
        </template>
        <!-- 底部 -->
        <template v-slot:bottom="{ item }">
          <view class="bottom" v-show="item != null">
            <text class="title">{{ item.title }}</text>
          </view>
        </template>
      </ml-swiper-v3>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        lists: [], // 资源数据
        datas: {0:[],1:[]}, // 点赞、收藏等数据
        count: 0, // 计数器
        context: null,
        type: "v3" // 使用的组件版本,v2:ml-swiper-v2、v3:ml-swiper-v3
      }
    },
    onHide() {
      if (this.context) { this.context?.pause(); }
    },
    onLoad() {
      // (模拟请求后台获取数据)
      uni.showToast({ title: "加载中...", icon: "loading", duration: 1500 });
      setTimeout(() => {
        let size = this.type == "v3" ? 50 : 2;
        for (var i = 0; i < size; i++) {
          this.getList().forEach((item) => {
            this.count = this.count + 1;
            item.title = `【${this.count}】` + item.title;
            this.lists.push(item);
          });
        }
        uni.hideToast();
      }, 1500);
    },
    methods: {
      // @onplay
      onplay(event) {
          this.context = event?.context;
      },
      // @avatarClick 
      avatarClick() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        uni.showToast({ title: "点击头像", icon: "none", duration: 1000 });
      },
      // @comment
      comment(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        uni.showToast({ title: "查看评论", icon: "none", duration: 1000 });
      },
      // @forward
      forward(_id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        uni.showToast({ title: "转发", icon: "none", duration: 1000 });
      },
      // @iconClick
      iconClick(type, id) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        this.datas[type] = (this.datas[type] || []);
        let index = this.datas[type]?.indexOf(id);
        if (index >= 0) {
          this.datas[type].splice(index, 1);
        } else {
          this.datas[type].push(id);
        }
      },
      // @loadmore
      loadmore() {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        uni.showToast({ title: "加载更多", icon: "none", duration: 1000 });
        for (var i = 0; i < 2; i++) {
          this.getList().forEach((item) => {
            this.count = this.count + 1;
            item.title = `【${this.count}】` + item.title;
            this.lists.push(item);
          });
        }
      },
      // @ondblclick
      ({ video }) {  // 这里的函数名可能会被转义掉,如果函数名为空 请看注释
        console.log(video?.videoId);
        this.iconClick(0, video?.videoId);
      },
      getList() {
        return [
          {
            videoId: this.lists.length + 1,
            title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "陌路",
              genderName: "男"
            }
          },
          {
            videoId: this.lists.length + 2,
            title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4",
            uploadTime: "2023-10-02 09:41",
            ipLocation: "贵州",
            author: {
              authorId: 102,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "御姐呀",
              genderName: "女"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: this.lists.length + 3,
            title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
            uploadTime: "2023-08-23 00:41",
            ipLocation: "广州",
            author: {
              authorId: 103,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "野花猫",
              genderName: "女"
            }
          },
          {
            videoId: this.lists.length + 4,
            title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-07-02 14:41",
            ipLocation: "山西",
            author: {
              authorId: 104,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "蓝姬",
              genderName: "女"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          }
        ];
      }
    }
  }
</script>

<style scoped>
  .body {
    position: absolute;
    top: 0;
    margin: 0 auto;
  }

  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0 auto;
  }

  .icon {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
    margin: 8px auto;
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }

  .progress {
    /* #ifndef APP-NVUE */
    width: 80%;
    /* #endif */
    height: 2px;
  }

  .progress-box {
    /* #ifndef APP-NVUE */
    width: 100%;
    display: flex;
    color: #fff;
    /* #endif */
    align-items: center;
    justify-content: space-around;
  }

  .bottom {
    margin: 10px;
  }

  .title {
    color: #fff;
    font-size: 13px;
  }

  .wh-full {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0 auto;
    overflow: hidden;
  }
</style>

nvue代码示例

示例代码中用到了 uni-icons 请自行安装

<template>
  <view class="wh-full" :style="screenFull">
    <!-- 这里使用 v-if 是为了防止 请求后台时还没返回数据 导致组件初始化时没有视频资源 而出现错误 -->
    <ml-swiper-v3 v-if="lists && lists.length > 0" :list="lists" @loadmore="loadmore" @ondblclick="" @onplay="onplay">
      <!-- 右侧 -->
      <template v-slot:right="{ item }">
        <view class="right" v-if="item != null">
          <!-- 用户头像 -->
          <image v-if="item.author != null" class="userAvatar" :src="item.author.avatar" @click="avatarClick">
          </image>
          <!-- 点赞 -->
          <view class="icon">
            <uni-icons type="heart-filled" size="35" color="#fff" @tap="iconClick('0', item.videoId)" />
            <text class="icon-val">666</text>
          </view>
          <!-- 评论 -->
          <view class="icon">
            <uni-icons type="chat-filled" size="35" color="#fff" @tap="comment(item.videoId)" />
            <text class="icon-val">668</text>
          </view>
          <!-- 收藏 -->
          <view class="icon">
            <uni-icons type="star-filled" size="35" color="#fff" @tap="iconClick('1', item.videoId)" />
            <text class="icon-val">888</text>
          </view>
          <!-- 转发 -->
          <view class="icon">
            <uni-icons type="redo-filled" size="35" color="#fff" @tap="forward(item.videoId)" />
            <text class="icon-val">999</text>
          </view>
        </view>
      </template>
      <!-- 底部 -->
      <template v-slot:bottom="{ item }">
        <view class="bottom" v-if="item != null">
          <text class="title">{{ item.title }}</text>
        </view>
      </template>
    </ml-swiper-v3>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        lists: [], // 资源数据
        datas: {0:[],1:[]}, // 点赞、收藏等数据
        count: 0, // 计数器
        context: null,
      }
    },
    computed: {
      screenFull: function() {
        const uniWin = uni.getSystemInfoSync();
        const maxWidth = uniWin.windowWidth;
        const maxHeight = uniWin.windowHeight;
        return `width: ${maxWidth}px; height: ${maxHeight}px;`;
      }
    },
    onHide() {
      if (this.context) { this.context?.pause(); }
    },
    onLoad() {
      this.datas["0"] = [], this.datas["1"] = [];
      // (模拟请求后台获取数据)
      uni.showToast({ title: "加载中...", icon: "loading", duration: 1500 });
      setTimeout(() => {
        let size = 50;
        for (var i = 0; i < size; i++) {
          this.getList().forEach((item) => {
            this.count = this.count + 1;
            item.title = `【${this.count}】` + item.title;
            this.lists.push(item);
          });
        }
        uni.hideToast();
      }, 1500);
    },
    methods: {
      // @onplay
      onplay(event) {
        this.context = event?.context;
      },
      avatarClick() {
        uni.showToast({ title: "点击头像", icon: "none", duration: 1000 });
      },
      comment(_id) {
        uni.showToast({ title: "查看评论", icon: "none", duration: 1000 });
      },
      forward(_id) {
        uni.showToast({ title: "转发", icon: "none", duration: 1000 });
      },
      iconClick(type, _id) {
        if (type == "0") {
          uni.showToast({ title: "点赞", icon: "none", duration: 1000 });
        } else if(type == "1") {
          uni.showToast({ title: "收藏", icon: "none", duration: 1000 });
        }
      },
      loadmore() {
        uni.showToast({ title: "加载更多", icon: "none", duration: 1000 });
        for (var i = 0; i < 2; i++) {
          this.getList().forEach((item) => {
            this.count = this.count + 1;
            item.title = `【${this.count}】` + item.title;
            this.lists.push(item);
          });
        }
      },
      ({ video }) {
        console.log(video?.videoId);
        this.iconClick(0, video?.videoId);
      },
      getList() {
        return [
          {
            videoId: this.lists.length + 1,
            title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "陌路",
              genderName: "男"
            }
          },
          {
            videoId: this.lists.length + 2,
            title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4",
            uploadTime: "2023-10-02 09:41",
            ipLocation: "贵州",
            author: {
              authorId: 102,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "御姐呀",
              genderName: "女"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: this.lists.length + 3,
            title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
            uploadTime: "2023-08-23 00:41",
            ipLocation: "广州",
            author: {
              authorId: 103,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "野花猫",
              genderName: "女"
            }
          },
          {
            videoId: this.lists.length + 4,
            title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-07-02 14:41",
            ipLocation: "山西",
            author: {
              authorId: 104,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "蓝姬",
              genderName: "女"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          }
        ];
      }
    }
  }
</script>

<style scoped>
  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0;
  }

  .icon {
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
    margin: 8px 0;
    /* #ifndef APP-NVUE */
    display: flex;
    margin: 8px auto;
    /* #endif */
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }

  .progress {
    /* #ifndef APP-NVUE */
    width: 80%;
    /* #endif */
    height: 2px;
  }

  .progress-box {
    /* #ifndef APP-NVUE */
    width: 100%;
    display: flex;
    color: #fff;
    /* #endif */
    align-items: center;
    justify-content: space-around;
  }

  .bottom {
    margin: 10px;
  }

  .title {
    color: #fff;
    font-size: 13px;
  }

  .wh-full {
    /* #ifndef APP-NVUE */
    width: 100%;
    height: 100%;
    /* #endif */
    padding: 0;
    margin: 0;
    overflow: hidden;
  }
</style>

APP端完整示例

ml-swiper-v3 示例

属性和方法

  /**
   * mlSwiperV3 视频滑动组件
   * @description 仿抖音短视频上下滑动组件,支持APP、小程序、H5
   * @property {Array} list 资源数据列表
   * @property {String} width 组件宽度,默认设备宽度
   * @property {String} height 组件高度,默认设备高度
   * @property {Number} criticalVal 临界值
   * @property {Boolean} progress 是否显示进度条
   * @property {Boolean} duration 是否显示播放时间
   * @property {Boolean} customProgress 自定义进度条
   * @property {Boolean} autoChange 是否自动切换下一个
   * @property {Number} slideDuration 滑动动画时长
   * @property {String} showFullscreen always | auto | none 显示全屏观看,APP仅支持 always | none
   * @property {Boolean} customFullscreen 是否自定义全屏观看按钮
   * @property {Number} startIndex 开始索引,从第 n 个资源开始
   * @property {Boolean} customInitTip 自定义初始化文本插槽
   * @property {String} initTipText 初始化提示文本,默认 初始化中...
   * 
   * @event {Function} onchange 当视频上下滑动时,触发 onchange(event)事件:event = {index, context, video}
   * @event {Function} onplay 当前视频播放时,触发 onplay(event)事件:event = {index, context, video, playing}
   * @event {Function} onpause(event) 当前视频暂停时,触发 onpause事件 event = {index, context, video, playing}
   * @event {Function} (event) 当前视频播放结束时,触发 onpause事件 event = {index, context, video, playing}
   * @event {Function}  视频进度条变化时触发ontimeupdate事件,可用来自定义进度条 event事件
   * @event {Function}  当前视频播放出现缓冲时,触发 事件 event = {index, context, video}
   * @event {Function} onerror 当前视频播放出错时,触发 onerror事件 event = {index, context, video, error, event}
   * @event {Function} onclick 当单击视频时触发onclick事件 event = {index, context, video, playing}
   * @event {Function}  当双击视频时触发ondblclick事件 event = {index, context, video, playing}
   * @event {Function} loadmore 当 list.length - currentIndex >= criticalVal时触发加载更多事件
   * @event {Function} longTap 视频长按事件 event = {index, context, video, playing}
   * @event {Function} fullscreenchange(event) 当视频进入和退出全屏时触发 event = {fullScreen, direction},direction取为 vertical 或 horizontal
   * @event {Function} fullscreenclick(event) 全屏时的点击事件 event = { screenX, screenY, screenWidth, screenHeight }
   */
<template>
  <view class="wh-full" :style="screenFull">
    <!-- 这里使用 v-if 是为了防止 请求后台时还没返回数据 导致组件初始化时没有视频资源 而出现错误 -->
    <ml-swiper-v3 v-if="lists && lists.length > 0" showFullscreen="always" @fullscreenchange="fullscreenchange"
      @longTap="longTap" :list="lists" @loadmore="loadmore" @ondblclick="dblclick" @onplay="onplay">
      <!-- 底部 -->
      <template v-slot:fullscreen>
        <view> <text>全屏</text> </view>
      </template>
      <template v-slot:bottom="{ item,index }">
        <view class="bottom" v-if="item != null">
          <view class="bottom-box">
            <view class="author">
              <image class="avatars" src="../../static/logo.png" mode="aspectFit"></image>
              <text style="color: aliceblue;">作者</text>
            </view>
            <text style="color: aliceblue;border-radius: 20px;padding: 5px;background: #ff2720;">关注</text>
          </view>
          <view class="text-title">
            <view class="">
              <text style="color: aliceblue;">这里是标题</text>
            </view>
            <view class="">
              <text style="color: grey;"> 【{{ index }}】{{ item.title }} 这里是副标题副标题副标题副标题副标题副标题副标题</text>
            </view>
          </view>
          <view class="bottom-tool">
            <input type="text" placeholder="说点什么" class="edit" />
            <uni-icons type="heart-filled" size="35" color="#fff" />
            <uni-icons type="star-filled" size="35" color="#fff" />
            <uni-icons type="chat-filled" size="35" color="#fff" />
          </view>
        </view>
      </template>
    </ml-swiper-v3>
  </view>
</template>

<script>
  export default {
    props: {},
    data() {
      return {
        lists: [], // 资源数据
        datas: {
          0: [],
          1: []
        }, // 点赞、收藏等数据
        count: 0, // 计数器
        context: null,
      }
    },
    computed: {
      screenFull: function() {
        const uniWin = uni.getSystemInfoSync();
        const maxWidth = uniWin.windowWidth;
        const maxHeight = uniWin.windowHeight;
        return `width: ${maxWidth}px; height: ${maxHeight}px;`;
      }
    },
    onHide() {
      if (this.context) {
        this.context?.pause();
      }
    },
    created() {
      this.datas["0"] = [], this.datas["1"] = [];
      let list = [];

      list.push({
        videoId: 0,
        title: `【首个】多个美女带着遮阳帽出去散步自拍视频,好好看。`,
        // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
        url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
        uploadTime: "2023-07-02 14:41",
        ipLocation: "山西",
        author: {
          authorId: 104,
          avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
          nickName: "蓝姬",
          genderName: "女"
        }
      });
      this.getList().forEach((item) => list.push(item));
      this.lists = [...this.lists, ...list];
      console.log(list)
    },
    methods: {
      longTap(event) {
        console.log("组件长按事件:", event);
        uni.showToast({
          title: "组件长按事件",
          icon: "none",
          duration: 1000
        });
      },
      fullscreenchange(event) {
        console.log("进入/退出全屏事件:", event);
      },
      fullscreenclick(event) {
        console.log("全屏点击事件:", event);
      },
      // @onplay
      onplay(event) {
        this.context = event?.context;
      },
      avatarClick() {
        uni.showToast({
          title: "点击头像",
          icon: "none",
          duration: 1000
        });
      },
      comment(_id) {
        uni.showToast({
          title: "查看评论",
          icon: "none",
          duration: 1000
        });
      },
      forward(_id) {
        uni.showToast({
          title: "转发",
          icon: "none",
          duration: 1000
        });
      },
      iconClick(type, _id) {
        if (type == "0") {
          uni.showToast({
            title: "点赞",
            icon: "none",
            duration: 1000
          });
        } else if (type == "1") {
          uni.showToast({
            title: "收藏",
            icon: "none",
            duration: 1000
          });
        }
      },
      loadmore() {
        uni.showToast({
          title: "加载更多",
          icon: "none",
          duration: 1000
        });
        for (var i = 0; i < 2; i++) {
          this.getList().forEach((item) => {
            this.count = this.count + 1;
            item.title = `【${this.count}】` + item.title;
            this.lists.push(item);
          });
        }
      },
      dblclick({
        video
      }) {
        console.log(video?.videoId);
        uni.showToast({
          title: "双击事件",
          icon: "none",
          duration: 1000
        });
        this.iconClick(0, video?.videoId);
      },
      getList() {
        return [{
            videoId: this.lists.length + 1,
            title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "陌路",
              genderName: "男"
            }
          },
          {
            videoId: this.lists.length + 2,
            title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4",
            uploadTime: "2023-10-02 09:41",
            ipLocation: "贵州",
            author: {
              authorId: 102,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "御姐呀",
              genderName: "女"
            }
          },
          {
            videoId: this.lists.length + 3,
            title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
            uploadTime: "2023-08-23 00:41",
            ipLocation: "广州",
            author: {
              authorId: 103,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "野花猫",
              genderName: "女"
            }
          },
          {
            videoId: this.lists.length + 4,
            title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
            // poster: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-07-02 14:41",
            ipLocation: "山西",
            author: {
              authorId: 104,
              avatar: "https://i02piccdn.sogoucdn.com/2acf176d90718d73",
              nickName: "蓝姬",
              genderName: "女"
            }
          }
        ];
      }
    }
  }
</script>

<style scoped>
  .author {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
  }

  .edit {
    width: 200px;
    height: 35px;
    margin-right: 20px;
    border-radius: 15px;
    background: #717171;
  }

  .bottom-tool {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .text-title {}

  .avatars {
    width: 30px;
    height: 30px;
    border-radius: 100%;
  }

  .bottom-box {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
  }

  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0;
  }

  .icon {
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
    margin: 8px 0;
    /* #ifndef APP-NVUE */
    display: flex;
    margin: 8px auto;
    /* #endif */
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }

  .progress {
    /* #ifndef APP-NVUE */
    width: 80%;
    /* #endif */
    height: 2px;
  }

  .progress-box {
    /* #ifndef APP-NVUE */
    width: 100%;
    display: flex;
    color: #fff;
    /* #endif */
    align-items: center;
    justify-content: space-around;
  }

  .bottom {
    /* #ifndef APP-NVUE */
    width: 95%;
    /* #endif */
    flex: 1;
    margin: 10px;
  }

  .title {
    color: #fff;
    font-size: 13px;
  }

  .wh-full {
    /* #ifndef APP-NVUE */
    width: 100%;
    height: 100%;
    /* #endif */
    padding: 0;
    margin: 0;
    overflow: hidden;
  }
</style>

ml-swiper-app 示例

属性和方法

  /**
   * mlSwiperApp 视频滑动组件
   * @description  APP端特有
   * @property {Array} list 资源数据列表
   * @property {Number} width 组件宽度,默认设备宽度
   * @property {Number} height 组件高度,默认设备高度
   * @property {Number} criticalVal 临界值
   * @property {Boolean} progress 是否显示进度条
   * @property {Boolean} autoChange 是否自动切换下一个
   * @property {String} showFullscreen always | auto | none 显示全屏观看,APP仅支持 always | none
   * @property {Boolean} custom 是否自定义视频组件
   * @property {Boolean} autoplay 是否自动播放
   * @property {Number} startIndex 开始索引,从第 n 个资源开始
   * 
   * @event {Function} onchange 当视频上下滑动时,触发 onchange(event)事件:event = {index, context, video}
   * @event {Function} onplay 当前视频播放时,触发 onplay(event)事件:event = {index, context, video, playing}
   * @event {Function} onpause(event) 当前视频暂停时,触发 onpause事件 event = {index, context, video, playing}
   * @event {Function} (event) 当前视频播放结束时,触发 onpause事件 event = {index, context, video, playing}
   * @event {Function}  视频进度条变化时触发ontimeupdate事件,可用来自定义进度条 event事件
   * @event {Function}  当前视频播放出现缓冲时,触发 事件 event = {index, context, video}
   * @event {Function} onerror 当前视频播放出错时,触发 onerror事件 event = {index, context, video, error, event}
   * @event {Function} onclick 当单击视频时触发onclick事件 event = {index, context, video, playing}
   * @event {Function}  当双击视频时触发ondblclick事件 event = {index, context, video, playing}
   * @event {Function} loadmore 当 list.length - currentIndex >= criticalVal时触发加载更多事件
   * @event {Function} longTap 视频长按事件 event = {index, context, video, playing}
   * @event {Function} fullscreenchange(event) 当视频进入和退出全屏时触发 event = {fullScreen, direction},direction取为 vertical 或 horizontal
   * @event {Function} fullscreenclick(event) 全屏时的点击事件 event = { screenX, screenY, screenWidth, screenHeight }
   * 
   * 插槽:
   * custom 为 true 时:
   *   default:可以自定义 video 视频组件;参数:item(当前数据)、index(当前索引)
   * 
   * custom 为 false 时:
   *  right:右侧插槽;参数:item(当前数据)、index(当前索引)
   *  bottom:底部插槽;参数:item(当前数据)、index(当前索引)
   *  default:默认插槽;参数:item(当前数据)、index(当前索引)
   * 
   * progress 为 false 并且 custom 为 false 时:
   *   progress:自定义进度条;参数:item(当前数据)、index(当前索引)
   */

如果方法中的方法名为空,说明方法名被浏览器转义掉了,为保证可以正常运行,请前往 readme.md 文档中复制响应源码测试

<template>
  <view :style="`width:${width}px;height:${height}px;overflow:hidden;`">
    <ml-swiper-app v-if="list.length > 2" :list="list" :width="width" :height="height" :custom="custom"
      :progress="progress" :criticalVal="criticalVal" :autoplay="autoplay" :startIndex="startIndex"
      :autoChange="autoChange" @change="onChange" @toupper="toupper" @tolower="tolower" @loadmore="loadmore"
      @ondblclick="dblclick" @onplay="onPlay" @onpause="onPause" @onended="ended" @onError="errored"
      @onWaiting="waiting" @onTimeUpdate="timeUpdated" @fullscreenchange="fullscreenchange"
      @fullscreenclick="fullscreenclick">

      <!-- custom="true":自定义 video 组件  -->
      <template v-if="custom" v-slot:default="{item}">
        <video :src="item.url" :title="item.title" :poster="item.poster" :autoplay="false" :controls="false"
          :loop="true" :show-center-play-btn='true' play-btn-position="center" :show-loading="false"
          :enable-progress-gesture="false" object-fit="contain" :style="`width:${width}px;height:${height}px;`"></video>
      </template>

      <!-- custom="false":使用默认 video 组件,当 custom="true",以下插槽失效,需要自定义实现  -->
      <!-- 默认插槽:default = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:default="{ index }">
        <view> <text style="color: #fff;">{{ index + 1 }} / {{ list.length }}</text> </view>
      </template>
      <!-- 右侧插槽:right = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:right="{ item }">
        <view class="right">
          <image class="userAvatar" :src="item?.author?.avatar"></image>
          <!-- 喜欢 -->
          <view class="icon">
            <uni-icons type="heart-filled" size="35" color="#ff0004" />
            <text class="icon-val">666</text>
          </view>
          <!-- 评论 -->
          <view class="icon">
            <uni-icons type="chat-filled" size="35" color="#fff" />
            <text class="icon-val">668</text>
          </view>
          <!-- 收藏 -->
          <view class="icon">
            <uni-icons type="star-filled" size="35" color="#ff0" />
            <text class="icon-val">888</text>
          </view>
          <!-- 转发 -->
          <view class="icon">
            <uni-icons type="redo-filled" size="35" color="#fff" />
            <text class="icon-val">999</text>
          </view>
        </view>
      </template>
      <!-- 底部插槽:bottom = {item: "当前资源数据", index: "当前资源索引"} -->
      <template v-slot:bottom="{ item }">
        <view :style="`width:${width}px;`">
          <text style="color: #fff;">{{ item.title }}</text>
        </view>
      </template>

      <!-- progress="false":不使用默认进度条,自定义进度条,需要配合 @ontimeupdate 事件获取当前播放时间 -->

    </ml-swiper-app>
  </view>
</template>

<script>
  import mlSwiperApp from '../../uni_modules/ml-swiper-v3/components/ml-swiper-v3/ml-swiper-app.vue';
  export default {
    components: {
      mlSwiperApp
    },
    data() {
      return {
        list: [], // 视频数据列表:{url:"视频资源地址", poster:"预览图,尽量给出,否则上下滑动时会黑屏", title:"视频标题"}
        custom: false, // 是否自定义 video 组件, 默认false
        progress: true, // 是否显示进度条,默认 true
        autoChange: true, // 是否自动播放下一个
        criticalVal: 2, // 临界值,默认 2 
        autoplay: true, // 初次加载时视频是否自动播放
        startIndex: 0, // 开始索引,从第 n 个资源开始,默认0
        width: uni.getSystemInfoSync().windowWidth, // 组件宽度
        height: uni.getSystemInfoSync().windowHeight // 组件高低
      }
    },
    created() {
      this.list = this.getList();
    },
    mounted() {},
    onHide() { // 如果需要离开页面暂停播放,尽量提供 onHide 
      this.pause();
    },
    onUnload() {
      this.pause();
      this.list = [];
      this.context = null;
    },
    methods: {
      onChange(e) { // onChange
        console.log(" === onChange 滑动 ===> ", e);
        console.log(this.custom)
        console.log("\r\n");
        if (this.custom) {
          this.play();
        }
      },
      toupper(e) { // toupper
        console.log(" === toupper 到顶了 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "到顶了",
          icon: 'none'
        });
      },
      tolower(e) { // tolower
        console.log(" === tolower 到底了 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "到底了",
          icon: 'none'
        });
      },
      loadmore(e) { // loadmore
        console.log(" === loadmore 加载更多 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "加载更多",
          icon: 'none'
        });
        this.list = [...this.list, ...this.getList()];
      },
      dblclick(e) { // dblclick
        console.log(" === dblclick 双击 ===> ", e);
        console.log("\r\n");
        uni.showToast({
          title: "双击",
          icon: 'none'
        });
      },
      onPlay(e) { // onPlay
        this.context = e.context;
        console.log(" === onPlay 开始播放 ===> ", e);
        console.log("\r\n");
      },
      onPause(e) { // onPause
        console.log(" === onPause 暂停播放 ===> ", e);
        console.log("\r\n");
      },
      ended(e) { // ended
        console.log(" === ended 播放结束 ===> ", e);
        console.log("\r\n");
      },
      errored(e) { // errored
        console.error(e);
      },
      waiting(e) { // waiting
        console.log(" === waiting 出现缓冲 ===> ", e);
        console.log("\r\n");
      },
      timeUpdated(e) { // timeUpdated
        console.log(" === timeUpdated 更新时长 ===> ", e);
      },
      fullscreenchange(e) { // fullscreenchange
        console.log(" === fullscreenchange 全屏变更 ===> ", e);
        console.log("\r\n");
      },
      fullscreenclick(e) { // fullscreenclick
        console.log(" === fullscreenclick 全屏点击 ===> ", e);
        console.log("\r\n");
      },
      pause() { // pause
        if (this.context?.pause) {
          this.context.pause();
        }
      },
      play() { // play
        if (this.context?.play) {
          this.context.play();
        }
      },
      // getList
      getList() {
        return [{
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `抖音美女主播,JK超短裙学生妆美女跳舞展示,爱了爱了。`,
            poster: "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028",
              nickName: "陌路",
              genderName: "男"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `图片列表测试。`,
            poster: "http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028",
              nickName: "陌路",
              genderName: "男"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `御姐美女抖音作品,来个自拍视频把,好美啊。`,
            poster: "http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/10/02/09/BMjAyMDEwMDIwOTAwMDlfMTIyMjc0NTk0Ml8zNjk3Mjg0NjcxOF8xXzM=_b_B28a4518e86e2cf6155a6c1fc9cf79c6d.mp4",
            uploadTime: "2023-10-02 09:41",
            ipLocation: "贵州",
            author: {
              authorId: 102,
              avatar: "http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028",
              nickName: "御姐呀",
              genderName: "女"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `图片列表测试。`,
            poster: "http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028",
            url: "https://txmov2.a.yximgs.com/upic/2020/11/08/19/BMjAyMDExMDgxOTQxNTlfNTIzNDczMzQ0XzM4OTQ1MDk5MTI4XzFfMw==_b_Bc770a92f0cf153407d60a2eddffeae2a.mp4",
            uploadTime: "2023-11-08 19:41",
            ipLocation: "上海",
            author: {
              authorId: 101,
              avatar: "http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028",
              nickName: "陌路",
              genderName: "男"
            },
            imgList: [
              'http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
              'http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028',
              'http://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
              'http://gips3.baidu.com/it/u=119870705,2790914505&fm=3028',
              'http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028',
              'http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028'
            ]
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `抖音主播可爱妹子新学的舞蹈,超可爱的美女主播。`,
            poster: "http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028",
            url: "https://txmov6.a.yximgs.com/upic/2020/08/23/00/BMjAyMDA4MjMwMDMyNDRfMTYzMzY5MDA0XzM0ODI4MDcyMzQ5XzFfMw==_b_B9a1c9d4e3a090bb2815994d7f33a906a.mp4",
            uploadTime: "2023-08-23 00:41",
            ipLocation: "广州",
            author: {
              authorId: 103,
              avatar: "http://gips0.baidu.com/it/u=2298867753,3464105574&fm=3028",
              nickName: "野花猫",
              genderName: "女"
            }
          },
          {
            videoId: Math.ceil(Math.random() * 689),
            likes: Math.ceil(Math.random() * 689),
            stars: Math.ceil(Math.random() * 689),
            isLike: 0,
            title: `多个美女带着遮阳帽出去散步自拍视频,好好看。`,
            poster: "http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028",
            url: "https://alimov2.a.yximgs.com/upic/2020/07/02/14/BMjAyMDA3MDIxNDUyMDlfOTExMjIyMjRfMzE1OTEwNjAxNTRfMV8z_b_Bf3005d42ce9c01c0687147428c28d7e6.mp4",
            uploadTime: "2023-07-02 14:41",
            ipLocation: "山西",
            author: {
              authorId: 104,
              avatar: "http://gips2.baidu.com/it/u=3944689179,983354166&fm=3028",
              nickName: "蓝姬",
              genderName: "女"
            }
          }
        ];
      }
    }
  }
</script>

<style scoped lang="scss">
  .text {
    font-size: 50px;
    color: #ff5918;
  }

  .right {
    width: 50px;
    padding: 10px 0;
    margin: 0;
  }

  .icon {
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    justify-content: center;
  }

  .icon-val {
    color: #fff;
    font-size: 13px;
    text-align: center;
  }

  .userAvatar {
    width: 40px;
    height: 40px;
    border-radius: 100%;
  }
</style>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

APP端需要配置manifest.json -> App模块配置 -> 勾选VideoPlay(视频播放)。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问