抖音小程序:在 video-play 组件中 自定义进度条

357 阅读3分钟

一、前置信息

本文记录使用 uniapp内置组件slidervideo-player 实现自定义进度条

  • 功能描述:用户可通过拖动,或点击进度某点实现使视频播放到指定进度,在拖动中时间不会动态更新,只在拖动完毕以后更新

  • 成品图

    image.png

  • 需要用到的事件如下:

    • <video-player>
      • @timeupdate : 播放进度变化时触发,返回当前播放时间点及视频总时长,单位:秒(s)。event.detail = { currentTime, duration }

      • @seekcomplete : seek 完成时触发。返回 seek 完成后的播放时间点,单位:秒(s)。event.detail={position}。

    • <slider>
      • @change : 完成一次拖动后触发的事件,event.detail = {value: value}

      • @changing : 拖动过程中触发的事件,event.detail = {value: value}

  • 其他:

    • 本次没有使用原生引用<video-player>组件,只是在pages.json中加入了,

      "usingComponents": {
          "video-player":"ext://industry/video-player"
      }
      
    • 参考上条,"disableSwipeBack": true禁止左滑属性没有生效

二、正文

  • html主要结构如下:
    • 注意: 此处没写到<swiper>外面,因为属性禁止左滑没生效,写在外面在滑动过程中总是触发左滑返回,写在此处可正常拖动
    <swiper>
        <swiper-item>
            // video-player 组件参数仅展示本次所需
            <video-player 
                ...
                id="myVideo"
                ...
                @timeupdate="Addtimeupdate"
                ...
            />

            // 下面是自定义的进度条(.stop 防冒泡)
            // .barview 样式把进度条定到页面底部
            <view class="barview">
                <text>{{ currentTimeText }}</text>
                <slider
                  :value="progressPercent"
                  :min="0"
                  :max="100"
                  :blockSize="20"
                  backgroundColor="#fff"
                  @change.stop="sliderChange"
                  @changing.stop="this.isDrag=true"
                >
                </slider>
                <text>{{ totalTimeText }}</text>
            </view>
        </swiper-item>
    </swiper>
  • js代码
<script>
    export default{
        data(){
            return{
                isPlaying: false, // 视频播放标识 true 正在播放 false 暂停播放
                //进度条相关
                isDrag: false, // //进度条是否在拖拽
                progressPercent: 0, // 进度条百分比
                videoDuration: 0, // 视频总时长(以秒为单位)
                currentTime: 0, // 当前视频播放进度(以秒为单位,没用可以不要)
                currentTimeText: "00:00", // 进度条左侧-当前播放时间
                totalTimeText: "00:00", // 进度条右侧-总时长
            }
        },
        methods:{
            /**播放进度变化时触发,动态给进度条赋值 */
            Addtimeupdate(event) {
              // 如果进度条正在拖动,则停止在页面同步当前播放进度
              if (this.isDrag) return;

              const totalDuration = event.detail.duration ? event.detail.duration : this.videoDuration
              // 记录当前集的总时间(以秒为单位)
              this.videoDuration = totalDuration;
              // 获取总时长(去小数)
              this.totalTimeText = this.formatTime(totalDuration);

              const activeDuration = event.detail.currentTime || event.target.currentTime;
              // 记录当前时间(以秒为单位)
              this.currentTime = activeDuration
              // 设置当前时长(去小数)
              this.currentTimeText = this.formatTime(activeDuration);

              // 获取自定义进度条的当前进度值
              this.progressPercent = (activeDuration / totalDuration) * 100;
            },
            /** 本方法用于把秒转换为 mm:ss 的格式 */
            formatTime(seconds) {
              const minutes = Math.floor(seconds / 60);
              const secs = Math.floor(seconds % 60);
              return `${minutes}:${secs < 10 ? "0" : ""}${secs}`;
            },
            /** 拖拽结束时触发 */
            sliderChange(e) {
              const video = uni.createVideoContext("myVideo", this);
              if (!video) return;
              // 暂停视频
              video.pause()
              if(e.detail.value == 100){
                this.isDrag = false;
                // 如果直接拖动到结尾,直接调用视频播放结束事件,不必再跳转到指定时间
                this.videoEnded()
                return
              }
              // 避免用户直接点击进度条,触发不了changing事件(ios部分机型复现)
              this.isDrag = true;
              // 跳到指定时间点
              video.seek((e.detail.value / 100) * this.videoDuration);
            },
            /** 视频seek完成触发的回调 */
            seekcomplete(e){
               // 避免ios在切页面再回来时(部分机型偶现),自动触发本事件,导致bug
              if(this.isDrag){
                this.isDrag = false;
                // seek完判断视频状态
                if(this.isPlaying){
                  // 如果是播放状态就恢复播放
                  const video = uni.createVideoContext("myVideo", this);
                  if (!video) return;
                  // 播放视频
                  video.play()
                }else{
                  // 暂停情况下,拖动完手动更新进度条时间
                  // (自己模拟了event结构)
                  const myCurrentE = {
                    detail: {
                      currentTime: e.detail.position
                    }
                  }
                  this.Addtimeupdate(myCurrentE)
                }
              }
            },
        }
    }
</script>

划重点:

  • 为什么要在 @change 中先暂停,再在 @seekcomplete 中播放?

    • 模拟情况:在 @change 中暂停,seek完,再播放

    • bug:进度条闪烁,暂停完, @timeupdate 会被再偶现触发一次或n次,导致进度条频闪

  • @change 已经把 isDrag 改为 true 了,为什么在 @change 中还要再改一次?

    • 避免用户直接点击进度条,触发不了 @changing 事件(ios部分机型复现)
  • 拖到100的时候不可以让他自己播放到结束吗?

    • 不可以,会bug(具体记不清了)

结语

—————— 写完啦,感谢观看 ( ̄︶ ̄*)),欢迎指正 ——————