首页 前端知识 video 自定义视频播放控件

video 自定义视频播放控件

2024-05-26 01:05:25 前端知识 前端哥 225 977 我要收藏

ui设计的界面总是极具个性化的,要去修改插件中的视频控件的样式和布局太困难了,那就自己参照video原生事件,重写一个吧。

(效果图预览)


一、video标签的属性(props)

 html <video>标签 | 菜鸟教程

<video 
     ref="videoPlayer" 
     id="videoElement" 
     controls 
     autoplay
     :muted="isMute" 
     width="800px" 
     height="600px" 
>
     您的浏览器不支持video
</video>

参数说明:(更多属性参照上述菜鸟教程中的video标签)

  • controls:默认为true,即向用户展示视频控件(如播放、暂停按钮等)
  • autoplay:如果出现该属性,则视频在就绪后马上播放。
  • muted:是否静音,默认为true
  • width:设置视频播放器的宽度

二、video视频控件的触发事件 

video标签支持的多媒体事件(Media Events) | 菜鸟教程

<video 
    ref="videoPlayer" 
    id="videoElement" 
    controls 
    autoplay
    :muted="isMute" 
    width="100%" 
    height="100%" 
    @loadeddata="setVideoPoster($event)"
    @progress="videoProgress($event)"
    @pause="videoPause($event)"
    @play="videoPlay($event)"
    @timeupdate="videoTimeUpdate()"
    @ended="videoEnded()"
    @contextmenu="contextmenu"
>
    您的浏览器不支持video
</video>

<button @click="handlePlay">播放</button>
<button @click="handlePause">暂停</button>
<button @click="handleMute">切换静音</button>
<button @click="fullScreen">全屏</button>

data(){
    return{
        isMute: true // 默认静音 
    }
}

1、播放(onplay事件)

this.$refs.videoPlayer.play();

methods:{
    // 视频要开始播放时
    videoPlay(e){
        // ...触发该函数后视频会开始播放,我们可以做一些想做的事情,比如改变自定义播放按钮的样式等
    },

    // 自定义播放按钮中,触发视频的播放事件
    handelPlay(){
        this.$refs.videoPlayer.play();   // 会触发videoPlay()函数
    }

}

2、暂停(onpause事件)

this.$refs.videoPlayer.pause();

methods:{
    // 视频要暂停播放时
    videoPause(e){
        // ...触发该函数后视频会暂停播放
    },

    // 自定义播放按钮中,触发视频的播放事件
    handelPause(){
        this.$refs.videoPlayer.pause();   // 会触发videoPause()函数
    }

}

3、静音(muted属性)

(1)切换静音

 // 手动切换静音(点击(非拖拽)静音时,用户选择的音量不变)

handleMute() {

      this.isMute = !this.isMute;

},

(2)改变音量(volume属性

this.$refs.videoPlayer.volume = a; (a为从 0~1的数字)

// 这里用element的进度条写音量大小调节条
<el-slider v-model="curVolume" :show-tooltip="false" @input="changeVolume"></el-slider>

data(){
    return{
        curVolume: 0, // 默认音量为0
    }
},

methods:{
    changeVolume(val){
        this.curVolume = val;
        // 由于h5规定volum的值在0-1之间,所以这里要对获取到的val做一个处理(滑块的val是从0-100)
        this.$refs.videoPlayer.volume = val / 100;
        
        // 音量为0的时候,video控件为静音
        if ( val == 0 ) {
           this.isMute = true;
        } else {
           this.isMute = false;
        }
    }
}

4、全屏

fullScreen() {
    this.$refs.videoPlayer.webkitRequestFullScreen();
},

5、播放进度条

获取视频总时长(duration

var videoObj = this.$refs.videoPlayer;

videoObj.addEventListener('canplay', () => {

        this.totalT = videoObj.duration;

})

获取视频加载进度

HTML5视频 - 加载百分比?

javascript中使用video实现视频的缓冲,加载与下载,获取视频的时长

// 获取视频加载进度
videoProgress(e){
       var bf = this.$refs.videoPlayer.buffered;  
       var time = this.$refs.videoPlayer.currentTime;
       if ( bf.length != 0 ){
           var range = 0;
           while( !( bf.start(range) <= time && time <= bf.end(range) ) ) {
               range += 1;
           }
           var loadEndPercentage = ( bf.end(range) / this.playerVideo.duration ) * 100;  // 结束加载的百分比
           this.persentLoad = loadEndPercentage;
       }
},

(1)父组件调用

<template>
    <video 
      ref="videoPlayer" 
      @progress="videoProgress($event)"
      @timeupdate="videoTimeUpdate()"
    >
      您的浏览器不支持video
    </video>

    // 视频播放、加载进度条
    <ProgressLine 
      :presentT="presentT" 
      :totalT="totalT" 
      :persentLoad="persentLoad" 
      @changeCurrentTime="changeCurrentTime($event)"
      @changeCurrentWord="changeCurrentWord($event)"
    >
    </ProgressLine>

    // 播放时长、视频总时长
    <p>
      <span id="currentTime" ref="progressTimer">{{ videoCurrentTime }}</span>
      <span style="color: #ffffff;opacity: 0.3;">&nbsp;/&nbsp;</span>
      <span id="durationTime" ref="durationTimer">{{ videoTotalTime }}</span>
    </p>
</template>

import ProgressLine from './ProgressLine.vue';
export default {
    name: 'videoPage',
    components: {
        ProgressLine
    },
    data(){
        return{
            presentT: 0,  // 进度条的当前值,必须为number
            totalT: 0,    // 进度条的最大值,必须为number
            persentLoad: 0, // 视频加载进度 
            videoCurrentTime: '00:00', // 当前视频已播放时长
            videoTotalTime: '00:00',  // 视频总时长
        }
    },
    methods:{
        // 子组件传入的时间修改
        changeCurrentTime(data) {
            this.$refs.videoPlayer.currentTime = data;  // 点击进度条设置视频当前播放点
        },
        changeCurrentWord(data) {
            this.videoCurrentTime = this.formatTime(data);  // 当前播放时间显示文字
        },
        // 获取视频加载进度
        videoProgress(e){
            var bf = this.playerVideo.buffered;  
            var time = this.playerVideo.currentTime;
            if ( bf.length != 0 ){
                var range = 0;
                while( !( bf.start(range) <= time && time <= bf.end(range) ) ) {
                    range += 1; 
                }
                
                var loadEndPercentage = ( bf.end(range) / this.playerVideo.duration ) * 100;    // 结束加载的百分比
                this.persentLoad = loadEndPercentage;
            }
        },
        // 视频自动播放时
        videoTimeUpdate(){
            this.presentT = this.playerVideo.currentTime;   // 获取当前播放时长
            this.videoCurrentTime = this.formatTime(this.presentT);  // 时间格式化
        },
        // 时间格式化
        formatTime(t) {
            var m = parseInt(t % 3600 / 60)
            m = m < 10 ? '0' + m : m
            var s = parseInt(t % 60)
            s = s < 10 ? '0' + s : s
            return m + ':' + s
        },
    }
}

(2)进度条组件(播放进度条 和 加载进度条)

// ProgressLine.vue 进度条组件
<template>
    <div>
        <div class="line-background">
            <div class="time-line" @click="adjustProgress($event)">
                <div class="progress-round" ref="progressRound">
                    <div class="loading" ref="persentLoad" style="width: 0;"></div>   <!-- 加载进度条 -->
                    <div class="progress" ref="progress" @click="adjustProgress"></div>
                    <div class="round" ref="round" @mousedown="roundDrag"></div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: 'ProgressLine',
    props: {
        presentT: {},
        totalT: {},
        persentLoad: { default : 0 }
    },
    data() {
        return {
            // 进度条拖拽
            dragClick: false,
            // 鼠标/手指按下
            clickDown: false,
        }
    },
    created() {

    },
    watch: {
        // 侦听当前播放时长设置进度条
        presentT: {
            handler(newValue, oldValue) {
                // 未点击进度条
                if (this.dragClick == false && this.clickDown == false) {
                    this.$refs.progress.style.width = newValue / this.totalT * 100 + '%'
                    if ((newValue / this.totalT * 100 - 1.23) < 0) {
                        this.$refs.round.style.left = 0 + '%'
                    } else {
                        this.$refs.round.style.left = (newValue / this.totalT * 100) - 1.23 + '%'
                    }
                } else if (this.dragClick == true) {
                    this.dealWidth()
                    this.dragClick = false
                }
            }
        },
        persentLoad: {
            handler(newValue, oldValue) {
                this.$refs.persentLoad.style.width = ( newValue / 100 ) * 1300 + 'px'; 
            }
        }
    },
    methods: {
        progressData(data) {
            this.$emit('changeCurrentTime', data)
            this.$emit('changeCurrentWord', data)
        },
        // 进度条位置和圆点定位处理
        dealWidth() {
            this.$refs.progress.style.width = this.progressWidth / this.$refs.progressRound.offsetWidth * 100 + '%'
            if ((this.progressWidth / this.$refs.progressRound.offsetWidth * 100) - 1.23 < 0) {  // 圆点定位
                this.$refs.round.style.left = 0 + '%'
            } else {
                this.$refs.round.style.left = (this.progressWidth / this.$refs.progressRound.offsetWidth * 100) - 1.23 + '%'
            }
        },
        // 进度条点击
        adjustProgress(e) {
            this.dragClick = true
            e.preventDefault()
            const { left, width } = this.$refs.progressRound.getBoundingClientRect()  // 进度条到屏幕距离及进度条的宽度
            this.progressWidth = e.clientX - left
            if (this.progressWidth < 0) {//进度条边界值计算情况
                this.progressWidth = 0
            } else if (this.progressWidth >= width) {
                this.progressWidth = width
            } else {
                this.progressWidth = e.clientX - left // e.clientX:鼠标点击的位置到屏幕最左侧的距离
            }
            this.dealWidth()
            this.progressData((this.progressWidth / width) * this.totalT)
        },
        // 进度条圆点拖拽
        roundDrag(event) {
            event.preventDefault()
            const offsetX = event.offsetX
            this.dragClick = true
            this.clickDown = true  // 解决圆点拖拽进度条长度抖动
            document.onmousemove = (e) => {  // 给圆点添加移动事件
                e.preventDefault()// 阻止进度条拖拽时屏幕原有的滑动功能
                const X = e.clientX  // 获取圆点离屏幕的距离
                const { left, width } = this.$refs.progressRound.getBoundingClientRect()
                const ml = X - left  // 进度条长度:圆点离屏幕的距离减去进度条最左边离屏幕的距离
                if (ml <= 0) {  // 进度条长度最小和最大值的界定
                    this.progressWidth = 0
                } else if (ml >= width) {
                    this.progressWidth = width
                } else {
                    this.progressWidth = ml
                }
                this.progressData((this.progressWidth / width) * this.totalT)  //视频播放时间
                this.dealWidth()
            }
            // 抬起鼠标,结束移动事件
            document.onmouseup = () => {
                document.onmousemove = null
                document.onmouseup = null
                this.clickDown = false
            }
        },
    }
}
</script>
<style lang="less" scoped>
.line-background {
    width: 100%;
    height: 10px;
    background-color: rgba(255, 255, 255, 0.3);

    .time-line {
        width: 100%;
        height: 10px;
        background-color: #565651;

        .progress-round {
            cursor: pointer;
            width: 100%;
            position: relative;
            display: flex;

            .loading {
                height: 10px;
                background-color: rgba(255, 255, 255, 0.3);
            }

            .progress {
                position: absolute;
                top: 0;
                left: 0;
                width: 00%;
                height: 10px;
                background-color: #3d7eff;
            }

            .round {
                position: absolute;
                top: 50%;
                left: 0;
                transform: translateY(-50%);
                width: 16px;
                height: 16px;
                border-radius: 16px;
                background: #ffffff;
                box-shadow: -2px 0px 2px 2px rgba(3, 0, 0, 0.30);
            }
        }
    }
}
</style>

6、视频中禁用右键(可以禁止用户下载视频)

// 在视频中禁用右键(禁止用户下载)
contextmenu(e){
    e.returnValue = false;
},

7、设置倍速播放

 this.$refs.videoPlayer.playbackRate = rate;   // rate 一般在[2.0,1.75,1.5,1.0,0.75,0.5]范围

8、隐藏下载按钮

(1)@contextmenu="contextmenu"

(2)controlslist="nodownload"

<video width="100%" height="100%" controls @contextmenu="contextmenu" controlslist="nodownload">
      <source :src="src" type="video/mp4">
      您的浏览器不支持 video 标签。
</video>

// 在视频中禁用右键(禁止用户下载)
contextmenu(e){
   e.returnValue = false;
},

 

转载请注明出处或者链接地址:https://www.qianduange.cn//article/9613.html
标签
音视频
评论
发布的文章

html5怎么实现语音搜索

2024-06-01 10:06:32

HTML5

2024-02-27 11:02:15

HTML - 头部元素

2024-06-01 10:06:06

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!