如何封装个video插件。自定义进度条点击拖拽,播放暂停,播放倍速

问题:vue3开发嵌入式,播放器在浏览器中是正常的。但嵌入到app里,app不适配,无法全屏,无法设置倍速,controls在视频播放时点击视频无法弹出

1.是不是播放器问题导致不适配?

         期间试了vue-video-play,vue3-video-play等视频插件,也是相同情况

 2.网上没找到相关问题,自己搞吧

代码效果

  注意

1.使用了vant图标,先安装vant

2.视频链接,需要自己填

3.引入的useDraggable,就是封装的拖拽js

4.只适合移动端,因为到移动端有些问题,做了样式调整。动作事件也是移动端的

5.template中class类名不要乱改,封装的拖拽js代码里有获取

template代码

<template>

  <div class="all_box">

    <div class="videoBox">

      <video ref="videoPlayer"

        src=""></video>

    </div>

    <div class="controlsBox">

      <div class="progress-box" @click="onProgress($event)" ref="progressBox">

        <div class="progress" :style="{ width: `${progressLength}px` }"></div>

        <div ref="draggableDiv" class="it-layout-aside" :style="{ left: `${progressLength}px` }"></div>

      </div>

      <div class="playBtn" @click="clickPlay()">

        <van-icon name="pause-circle-o" size="30" v-if="showPlay" />

        <van-icon name="play-circle-o" size="30" v-else />

      </div>

      <div class="progress-time">{{ timeString }}</div>

      <div class="selRate">

        <select class="selRate_selet" v-model="selRate" @change="onSelRate()">

          <option value=0.5>0.5</option>

          <option value=1 selected>1.0</option>

          <option value=1.25>1.25</option>

          <option value=1.5>1.5</option>

          <option value=2>2.0</option>

        </select>

      </div>

    </div>

  </div>

</template>

script代码

<script setup>

import { ref, unref, } from "vue";

import { useDraggable } from './components/CommentResponse/Use'

const videoPlayer = ref(null);

const progressBox = ref(null)

const showPlay = ref(false)

const progressLength = ref(0)

const timeString = ref('00:00/00:00')

const selRate = ref(1)

//拖动进度条

const moveing = (val) => {

  showPlay.value = true;

  clearInterval(progressTimer)

  var length = val - progressBox.value.offsetLeft

  var percent = length / progressBox.value.offsetWidth

  unref(videoPlayer).currentTime = percent * unref(videoPlayer).duration

  unref(videoPlayer).play()

  progressTimer = setInterval(changeProgress, 60)

}

const draggableDiv = useDraggable(moveing)

//倍速

const onSelRate = () => {

  unref(videoPlayer).playbackRate = selRate.value

}

//启动|停止

const clickPlay = () => {

  if (showPlay.value) {

    unref(videoPlayer).pause();

    showPlay.value = false;

    clearInterval(progressTimer)

  } else {

    unref(videoPlayer).play();

    showPlay.value = true;

    progressTimer = setInterval(changeProgress, 60)

  }

}

var progressTimer = null // 进度 timer

// 计算时长,注意:这里的 padStart 是 es7 语法

const parseTime = (value) => {

  if (!value) return ''

  let interval = Math.floor(value)

  let minute = (Math.floor(interval / 60)).toString().padStart(2, '0')

  let second = (interval % 60).toString().padStart(2, '0')

  return `${minute}:${second}`

}

// 推进进度条

const changeProgress = () => {

  if (unref(videoPlayer).currentTime && unref(videoPlayer).duration) {

    if (parseTime(unref(videoPlayer).currentTime) === parseTime(unref(videoPlayer).duration)) {

      progressLength.value = 0;

      timeString.value = '00:00/00:00';

      showPlay.value = false;

    } else {

      let timeStr = parseTime(unref(videoPlayer).currentTime) + '/' + parseTime(unref(videoPlayer).duration)

      let percent = unref(videoPlayer).currentTime / unref(videoPlayer).duration

      progressLength.value = percent * 365;

      timeString.value = timeStr;

    }

  }

}

// 点击进度条的任意地方

const onProgress = (e) => {

  showPlay.value = true;

  clearInterval(progressTimer)

  var length = e.x - progressBox.value.offsetLeft

  var percent = length / progressBox.value.offsetWidth

  unref(videoPlayer).currentTime = percent * unref(videoPlayer).duration

  unref(videoPlayer).play()

  progressTimer = setInterval(changeProgress, 60)

}

</script>

css代码

<style scoped >

.all_box {

  margin: 30px;

}

.it-layout-aside {

  position: absolute;

  top: -4px;

  left: 0px;

  width: 13px;

  height: 13px;

  border-radius: 50%;

  background: rgb(0, 0, .5);

  opacity: 0.8;

  color: #fff;

  cursor: move;

  opacity: 1;

  z-index: 999;

}

video {

  width: 100%;

  height: 200px;

  margin: 0 auto;

  position: relative;

}

.controlsBox {

  background: #ccc;

  height: 50px;

  position: relative;

}

.progress-box {

  position: relative;

  background: rgba(0, 0, 0, 0.1);

  border-radius: 8px;

  overflow: hidden;

  cursor: pointer;

}

.progress {

  position: absolute;

  top: 0;

  left: 0;

  width: 0%;

  height: 100%;

  background: white;

  border-radius: 8px 0px 0px 8px;

}

.slider_circle {

  position: absolute;

  top: 0;

  left: 0;

  width: 20px;

  height: 50px;

  background: red;

}

.videoBox {

  width: 100%;

  min-width: 100%;

  max-width: 100%;

  height: 200px;

  position: relative;

  background: black;

}


 

.progress-box {

  width: 365px;

  height: 6px;

  position: absolute;

  bottom: 35px;

  right: 1%;

  overflow: visible;

}

.playBtn {

  width: 30px;

  height: 30px;

  position: absolute;

  bottom: 1px;

  left: 10px;

}

.progress-time {

  height: 30px;

  position: absolute;

  bottom: 1px;

  left: 50px;

  color: #f8f8f8;

  display: flex;

  align-items: center;

}

.selRate {

  width: 70px;

  height: 30px;

  position: absolute;

  bottom: 1px;

  left: 140px;

  display: flex;

  align-items: center;

  display: flex;

  align-items: center;

  justify-content: center;

}

.selRate_selet{

  height: 20px;

  width: 60px;

}

i {

  font-size: 40px;

  color: white;

}

</style>

封装的拖拽功能js

import { ref, unref, onMounted, onUnmounted } from "vue";

export const useDraggable = (moveing) => {

    // 声明一个 ref,用于存储 div 元素的引用

    const divRef = ref(null)

    // 声明一些变量,用于存储鼠标或触摸位置以及拖拽状态

    let offsetX = 0 // 鼠标点击或触摸点距离 div 左侧的偏移

    let offsetY = 0 // 鼠标点击或触摸点距离 div 顶部的偏移

    let isDragging = false // 是否正在拖拽中

    // 禁用页面滚动的函数

    const disablePageScroll = () => {

        document.body.style.overflow = 'hidden'

    }

    // 启用页面滚动的函数

    const enablePageScroll = () => {

        document.body.style.overflow = 'auto'

    }

    // 开始拖拽,禁用页面滚动

    const startDragging = () => {

        isDragging = true

        disablePageScroll()

    }

    // 停止拖拽,启用页面滚动,并稍后重新启用点击事件

    const stopDragging = () => {

        isDragging = false

        enablePageScroll()

        setTimeout(() => {

            if (divRef.value) {

                divRef.value.style.pointerEvents = 'auto'

            }

        }, 100)

    }

    // 处理鼠标移动或触摸移动事件

    const handleMouseMove = (event) => {

        requestAnimationFrame(() => {

            if (isDragging && divRef.value) {

                const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX

                const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY

                const x = clientX - offsetX

                const y = clientY - offsetY

                // 阻止事件传播,避免干扰正常滚动

                event.stopPropagation()

                event.preventDefault()

                // 获取浏览器窗口的最大可视区域宽度和高度

                // const maxX = window.innerWidth - (divRef.value.clientWidth || 0)

                const maxX = 360 - (divRef.value.clientWidth || 0)

                const maxY = window.innerHeight - (divRef.value.clientHeight || 0)

                // 设置 div 的位置,确保不超出窗口范围

                divRef.value.style.left = `${Math.min(maxX, Math.max(0, x))}px`

                // divRef.value.style.top = `${Math.min(maxY, Math.max(0, y))}px`

                divRef.value.style.top = `-4px`

                // 禁用 div 上的点击事件,以避免拖拽时触发点击事件

                divRef.value.style.pointerEvents = 'none'

                const tiao = document.querySelector('.progress')

                tiao.style.width = `${Math.min(maxX, Math.max(0, x))}px`

                // console.log(tiao.style, 111)

                //回调宽度

                moveing(Math.min(maxX, Math.max(0, x)))

            }

        })

    }

    // 处理鼠标松开或触摸结束事件

    const handleMouseUp = () => {

        // 停止拖拽,恢复点击事件

        stopDragging()

        // 移除鼠标移动事件和触摸移动事件的监听器

        document.removeEventListener('touchmove', handleMouseMove)

        document.removeEventListener('mousemove', handleMouseMove)

    }

    // 处理鼠标按下或触摸开始事件

    const handleMouseDown = (event) => {

        if (!divRef.value) return

        // 获取鼠标点击或触摸点相对于 div 左侧和顶部的偏移

        offsetX = 'touches' in event ? event.touches[0].clientX - divRef.value.offsetLeft : event.clientX - divRef.value.offsetLeft

        offsetY = 'touches' in event ? event.touches[0].clientY - divRef.value.offsetTop : event.clientY - divRef.value.offsetTop

        // 开始拖拽,添加鼠标移动和触摸移动事件监听器

        startDragging()

        document.addEventListener('mousemove', handleMouseMove, {

            passive: false, // 阻止默认滚动行为

        })

        document.addEventListener('touchmove', handleMouseMove, {

            passive: false, // 阻止默认滚动行为

        })

        // 添加鼠标松开和触摸结束事件监听器

        document.addEventListener('mouseup', handleMouseUp)

        document.addEventListener('touchend', handleMouseUp)

    }

    // 在组件挂载时,添加鼠标按下和触摸开始事件监听器

    onMounted(() => {

        if (divRef.value) {

            divRef.value.addEventListener('mousedown', handleMouseDown)

            divRef.value.addEventListener('touchstart', handleMouseDown)

        }

    })

    // 在组件卸载时,移除事件监听器

    onUnmounted(() => {

        if (divRef.value) {

            divRef.value.removeEventListener('mousedown', handleMouseDown)

            divRef.value.removeEventListener('touchstart', handleMouseDown)

        }

        document.removeEventListener('mouseup', handleMouseUp)

        document.removeEventListener('touchend', handleMouseUp)

    })

    // 返回 div 元素的引用,可以在组件中使用该引用来创建可拖拽的元素

    return divRef

}

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果您想要实现自定义的视频播放进度条暂停/播放按钮,可以按照以下步骤进行: 1. 在布局文件中添加 VideoView、SeekBar 和暂停/播放按钮: ``` <VideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> <SeekBar android:id="@+id/seek_bar" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/play_pause_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Pause" /> ``` 2. 在 Activity 中获取 VideoView、SeekBar 和暂停/播放按钮的引用: ``` VideoView videoView = findViewById(R.id.video_view); SeekBar seekBar = findViewById(R.id.seek_bar); Button playPauseButton = findViewById(R.id.play_pause_button); ``` 3. 设置 VideoView 的路径并开始播放: ``` videoView.setVideoPath("path/to/video.mp4"); videoView.start(); ``` 4. 为 VideoView 添加一个 OnPreparedListener,当视频准备好时,设置 SeekBar 的最大值和添加一个定时器来更新 SeekBar 的进度: ``` videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { seekBar.setMax(videoView.getDuration()); final Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { seekBar.setProgress(videoView.getCurrentPosition()); handler.postDelayed(this, 1000); } }; handler.postDelayed(runnable, 1000); } }); ``` 5. 为 SeekBar 添加一个 OnSeekBarChangeListener,当拖动 SeekBar 时,改变视频播放的位置: ``` seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { videoView.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); ``` 6. 为暂停/播放按钮添加一个 OnClickListener,当点击按钮时,暂停或继续播放视频: ``` playPauseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (videoView.isPlaying()) { videoView.pause(); playPauseButton.setText("Play"); } else { videoView.start(); playPauseButton.setText("Pause"); } } }); ``` 7. 为 VideoView 添加一个 OnCompletionListener,当视频播放完成时,将暂停/播放按钮的文本设置为“Play”: ``` videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { playPauseButton.setText("Play"); } }); ``` 以上就是实现自定义视频播放进度条暂停/播放按钮的方法。您可以根据您的需求自定义 SeekBar 的样式和暂停/播放按钮的图标。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值