一、安装依赖
yarn add video.js //安装video.js
yarn add videojs-contrib-hls // 播放流的话需要安装 hls插件
二、引入video.js
在plugins文件夹下创建 hls.js
import Vue from 'vue';
import 'videojs-contrib-hls';
const hls = require('videojs-contrib-hls');
Vue.use(hls);
在 nuxt.config.js 中引用video.js样式
css: ['video.js/dist/video-js.css']
三、在使用video.js的页面中引用 video.js
import Videojs from 'video.js'; // 引入Videojs
1、测试使用video.js
初始化videoJs
initVideo(nowPlayVideoUrl) {
let that = this; // 重定向this
// 这些options属性也可直接设置在video标签上,见 muted
let options = {
controls: true, //确定播放器是否具有用户可以与之交互的控件。没有控件,启动视频播放的唯一方法是使用autoplay属性或通过Player API。
// autoplay: "muted",//自动播放属性,muted:静音播放
preload: 'auto', //预加载 auto 自动
fluid: true, // 自适应宽高
language: 'zh-CN', // 设置语言
// poster: require('@/assets/images/train/1.png'), //播放前显示的视频画面,播放开始之后自动移除。通常传入一个URL
// inactivityTimeout: false,//显示时间
// width:'100%',
// height:'210px'
sources: [
// 注意,如果是以option方式设置的src,是不能实现 换台的 (即使监听了nowPlayVideoUrl也没实现)
{
src: nowPlayVideoUrl,
type: 'application/x-mpegURL', // 告诉videojs,这是一个hls流
},
],
controlBar: false,
};
// videojs的第一个参数表示的是,文档中video的id
let myPlayer = Videojs('videoPlayer', options, function onPlayerReady() {
console.log('onPlayerReady 中的this指的是:', this); // 这里的this是指Player,是由Videojs创建出来的实例
that.volumeProgress();
this.on('loadedmetadata', function () {});
// 视频播放时更新当前播放进度
this.on('timeupdate', function () {
that.durationimeVue(); // 更新播放进度
if (!that.ischange) {
// 当拖拽进度条是不更新进度条
that.cacheProgress();
}
});
// 视频 缓冲进度
this.on('progress', function () {});
this.on('play', function () {
//播放
that.playstart = true; //控制显示播放暂停按钮
});
this.on('pause', function () {
//暂停
that.playstart = false; //控制显示播放暂停按钮
});
this.on('volumechange', function (e) {
//音量改变
if (!that.volumeChange) {
that.volumeProgress();
}
});
});
that.myPlayer = myPlayer
温馨提示;
onPlayerReady() 方法中是video 初始化完成后触发的事件,this.on('××××××××', function () {}); 是初始化完成后或视频播放过程中 触发的事件;
监听事件\ this 指的是video实例
this.on('suspend', function() {//延迟下载
console.log("延迟下载")
});
this.on('loadstart', function() { //客户端开始请求数据
console.log("客户端开始请求数据")
});
this.on('progress', function() {//客户端正在请求数据
console.log("客户端正在请求数据")
});
this.on('abort', function() {//客户端主动终止下载(不是因为错误引起)
console.log("客户端主动终止下载")
});
this.on('error', function() {//请求数据时遇到错误
console.log("请求数据时遇到错误")
});
this.on('stalled', function() {//网速失速
console.log("网速失速")
});
this.on('play', function() {//开始播放
console.log("开始播放")
});
this.on('pause', function() {//暂停
console.log("暂停")
});
this.on('loadedmetadata', function() {//成功获取资源长度
console.log("成功获取资源长度")
});
this.on('loadeddata', function() {//渲染播放画面
console.log("渲染播放画面")
});
this.on('waiting', function() {//等待数据,并非错误
console.log("等待数据")
});
this.on('playing', function() {//开始回放
console.log("开始回放")
});
this.on('canplay', function() {//可以播放,但中途可能因为加载而暂停
console.log("可以播放,但中途可能因为加载而暂停")
});
this.on('canplaythrough', function() { //可以播放,歌曲全部加载完毕
console.log("可以播放,歌曲全部加载完毕")
});
this.on('seeking', function() { //寻找中
console.log("寻找中")
});
this.on('seeked', function() {//寻找完毕
console.log("寻找完毕")
});
this.on('timeupdate', function() {//播放时间改变
console.log("播放时间改变")
});
this.on('ended', function() {//播放结束
console.log("播放结束")
});
this.on('ratechange', function() {//播放速率改变
console.log("播放速率改变")
});
this.on('durationchange', function() {//资源长度改变
console.log("资源长度改变")
});
this.on('volumechange', function() {//音量改变
console.log("音量改变")
});
常用方法
player.play(); // 播放
player.pause(); // 暂停
player.currentTime(); // 播放时长、可以根据此方法更改播放时间 player.currentTime(time);
player.duration(); // 视频总播放时长,
player.volume(); // 声音,可以根据 player.volume(volume) 改变声音 声音大小(0-1之间)
player.muted(Boolean) // 是否静音
player.requestPictureInPicture() //开启画中画
player.buffered(); 缓冲,就是返回下载了多少
player.bufferedPercent();百分比的缓冲
player.width(); //获取宽度 player.width(640) 设置宽度
player.height(); //获取高度 设置高度player.height(480);
视频状态与播放状态
player.controls();//是否有默认控制条
player.currentTime(); = value; //当前播放的位置,赋值可改变位置
player.startTime(); //一般为0,如果为流媒体或者不从0开始的资源,则不为0
player.duration(); //当前资源长度 流返回无限
player.paused(); //是否暂停
player.defaultPlaybackRate(); = value;//默认的回放速度,可以设置
player.playbackRate(); = value;//当前播放速度,设置后马上改变
player.played(); //返回已经播放的区域,TimeRanges,关于此对象见下文
player.seekable();//返回可以seek的区域 TimeRanges
player.ended();//是否结束
player.autoPlay(); //是否自动播放
player.loop(); //是否循环播放
自定义全屏退出全屏按钮
//开启全屏
let playerHtml = this.$refs.videobox; // 包裹video标签 bom
if (playerHtml.requestFullscreen) {
playerHtml = playerHtml.requestFullscreen();
} else if (playerHtml.mozRequestFullScreen) {
playerHtml = playerHtml.mozRequestFullScreen();
} else if (playerHtml.msRequestFullscreen) {
playerHtml = playerHtml.msRequestFullscreen();
} else if (playerHtml.webkitRequestFullscreen) {
playerHtml = playerHtml.webkitRequestFullScreen();
}
//退出全屏
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
四、附上代码
<template>
<div ref="videobox" :class="iFullScreen ? 'fullscreenbox ' : 'videobox'">
<video id="videoPlayer" class="video-js"></video>
<!-- 自定义组件 全屏 画中画 start -->
<div class="individualcontrol">
<div class="fullscreen">
<img
v-if="!iFullScreen"
class="w-20 h-20 transform rotate-180"
:src="require('@/assets/match/img/full.png')"
@click="fullscreen"
/>
<img
v-else
class="w-20 h-20 transform rotate-180"
:src="require('@/assets/match/img/quit.png')"
@click="quitscreen"
/>
</div>
<div class="PIP" @click="pipclick">
<img
class="w-20 h-20 transform rotate-180"
:src="require('@/assets/match/img/pip.png')"
/>
</div>
</div>
<!-- 自定义组件 全屏 画中画 end -->
<!-- 自定义组件 后退 暂停 播放 快进 播放进度条 下一个 start -->
<div class="controlBar">
<!-- 后退 start -->
<div class="retreat w-20" @click="currentTimeRetreat">
<img
class="w-20 h-20 transform rotate-180"
:src="require('@/assets/match/img/return.png')"
/>
</div>
<!-- 后退 end -->
<!-- 播放暂停 start-->
<div class="play w-20" @click="comtrol_play">
<img
v-if="playstart"
class="w-20 h-20 rotate-180"
:src="require('@/assets/match/img/pause.png')"
/>
<img
v-else
class="w-20 h-20 rotate-180"
:src="require('@/assets/match/img/play.png')"
/>
</div>
<!-- 播放暂停 end-->
<!-- 快进 start-->
<div class="fastforward w-20" @click="currentTimeFF">
<img
class="w-20 h-20 rotate-180"
:src="require('@/assets/match/img/return.png')"
/>
</div>
<!-- 快进 end-->
<!-- 进度条 start-->
<div class="broadcastpace">
<span>{{ video.currentTime }}</span>
<div class="progressbar">
<el-slider
v-model="Barvalue"
:show-tooltip="false"
@change="Barchange"
@mousedown.native="ischange = true"
@mouseup.native="ischange = false"
></el-slider>
</div>
<span>{{ video.duration }}</span>
</div>
<!-- 进度条 end-->
<!-- 声音 start-->
<div class="volume">
<div class="iconVolume" @click="volumeClick">
<img
v-if="video.volumeval != 0"
class="w-20 h-20"
:src="require('@/assets/match/img/loud.png')"
/>
<img
v-else
class="w-20 h-20"
:src="require('@/assets/match/img/turndown.png')"
/>
</div>
<div class="sliderVolume">
<el-slider
v-model="video.volumeval"
vertical
height="100px"
@change="volumeBarchange"
@mousedown.native="volumeChange = true"
@mouseup.native="volumeChange = false"
>
</el-slider>
</div>
</div>
<!-- 声音 end-->
<div></div>
<!-- 下一个 start-->
<div class="next">
<img
class="w-20 h-20 transform -rotate-90"
:src="require('@/assets/match/img/next.png')"
/>
</div>
<!-- 下一个 end-->
</div>
<!-- 自定义组件 后退 暂停 播放 快进 播放进度条 下一个 end -->
</div>
</template>
<script>
import { rateProgress } from '@/utils/index'; // 正则
import Videojs from 'video.js'; // 引入Videojs
export default {
data() {
return {
video: {
Src: 'https://vjs.zencdn.net/v/oceans.mp4', // 视频链接
// Src: 'http://××××××××××××:1935/vod//tstv1/20220310/1750003900_mp4/175000_3900_1000k_(1013_00_1275_36)_bvs_mp4/tzwj_video.m3u8',
currentTime: '0:00:00',
duration: '0:00:00',
volumeval: 0,
},
playstart: false, // 是否正在播放
controlBarhide: false, // 是否要隐藏控制条
iFullScreen: false, // 是否全屏
Barvalue: 0, // 视频 播放进度
ischange: false, // 是否拖拽进度条
volumeChange: false, // 拖动声音进度条
Barsetvalue: '',
CanDragDrop: true, // 是否可快进
myPlayer: null, // 播放器
};
},
mounted() {
this.initVideo(this.video.Src);
// 全屏监听
if (document.addEventListener) {
document.addEventListener(
'webkitfullscreenchange',
this.exitHandler,
false
);
document.addEventListener('mozfullscreenchange', this.exitHandler, false);
document.addEventListener('fullscreenchange', this.exitHandler, false);
document.addEventListener('MSFullscreenChange', this.exitHandler, false);
}
},
methods: {
initVideo(nowPlayVideoUrl) {
let that = this; // 重定向this
// 这些options属性也可直接设置在video标签上,见 muted
let options = {
controls: true, //确定播放器是否具有用户可以与之交互的控件。没有控件,启动视频播放的唯一方法是使用autoplay属性或通过Player API。
// autoplay: "muted",//自动播放属性,muted:静音播放
preload: 'auto', //预加载 auto 自动
fluid: true, // 自适应宽高
language: 'zh-CN', // 设置语言
sources: [
// 注意,如果是以option方式设置的src,是不能实现 换台的 (即使监听了nowPlayVideoUrl也没实现)
{
src: nowPlayVideoUrl,
// type: 'application/x-mpegURL', // 告诉videojs,这是一个hls流
},
],
controlBar: false,
};
// videojs的第一个参数表示的是,文档中video的id
let myPlayer = Videojs('videoPlayer', options, function onPlayerReady() {
console.log('onPlayerReady 中的this指的是:', this); // 这里的this是指Player,是由Videojs创建出来的实例
that.volumeProgress(); // 更新播放进度
this.on('loadedmetadata', function () {});
// 视频播放时更新当前播放进度
this.on('timeupdate', function () {
that.durationimeVue(); // 更新播放进度
if (!that.ischange) {
// 当拖拽进度条是不更新进度条
that.cacheProgress();
}
});
this.on('play', function () {
//播放
that.playstart = true; //控制显示播放暂停按钮
});
this.on('pause', function () {
//暂停
that.playstart = false; //控制显示播放暂停按钮
});
this.on('volumechange', function (e) {
//音量改变
if (!that.volumeChange) {
that.volumeProgress();
}
});
});
that.myPlayer = myPlayer;
},
// 视频播放暂停
comtrol_play() {
if (this.myPlayer.paused()) {
this.myPlayer.play();
} else {
this.myPlayer.pause();
}
},
// 视频后退
currentTimeRetreat() {
if (this.myPlayer.currentTime() > 10) {
let time = this.myPlayer.currentTime() - 10;
// 当前播放时间小于10 可以快退减10
this.myPlayer.currentTime(time);
} else {
// 否则就回到起点
this.myPlayer.currentTime(0);
}
},
// 视频快进
currentTimeFF() {
if (
this.myPlayer.currentTime() < this.myPlayer.duration() &&
this.CanDragDrop
) {
// 当前播放时间小于总时间 并且可以快进 可以快进加10
let time = this.myPlayer.currentTime() + 10;
this.myPlayer.currentTime(time);
}
},
//更新播放时长
durationimeVue() {
let result = this.myPlayer.duration(); // 视频总时长
let result2 = this.myPlayer.currentTime(); // 当前播放时长
this.video.duration = rateProgress(result);
this.video.currentTime = rateProgress(result2);
console.log(result);
},
// 视频播放进度条
cacheProgress() {
let duration = this.myPlayer.duration();
this.Barvalue = (this.myPlayer.currentTime() / duration) * 100;
},
// 进度条拖拽
Barchange(e) {
let currentProgress = (e / 100) * this.myPlayer.duration();
this.myPlayer.currentTime(currentProgress);
},
// 声音进度条拖拽
volumeBarchange(e) {
this.myPlayer.volume(e / 100);
},
// 声音进度条
volumeProgress() {
let volume = this.myPlayer.volume();
this.video.volumeval = volume * 100;
},
// 静音
volumeClick() {
if (this.video.volumeval != 0) {
this.myPlayer.volume(0);
} else {
this.myPlayer.volume(50 / 100);
}
},
// 全屏播放
fullscreen() {
let playerHtml = this.$refs.videobox;
if (playerHtml.requestFullscreen) {
playerHtml = playerHtml.requestFullscreen();
} else if (playerHtml.mozRequestFullScreen) {
playerHtml = playerHtml.mozRequestFullScreen();
} else if (playerHtml.msRequestFullscreen) {
playerHtml = playerHtml.msRequestFullscreen();
} else if (playerHtml.webkitRequestFullscreen) {
playerHtml = playerHtml.webkitRequestFullScreen();
}
// this.iFullScreen = true; // 改变是否全屏状态
},
// 退出全屏
quitscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
// this.iFullScreen = false; // 改变是否全屏状态
},
//监听esc退出全屏
exitHandler() {
if (this.iFullScreen) {
this.iFullScreen = false;
} else {
this.iFullScreen = true;
}
},
// 画中画
pipclick() {
let whetherNot = document.pictureInPictureElement;
console.log(whetherNot);
if (whetherNot) {
this.myPlayer.requestPictureInPicture();
} else {
this.myPlayer.requestPictureInPicture();
}
},
},
};
</script>
<style lang="scss" scoped>
...
</style>