一、安装指定版本是正常命令后加‘@版本号’
npm install [email protected] --save
二,组件封装的代码
<template>
<div class="live-video-wrapt">
<div id="videoUpdate" class="video-show" v-show="!isError">
<video
id="videoTV"
class="video-js vjs-default-skin vjs-big-play-centered vjs-16-9"
preload="auto"
webkit-playsinline="true"
playsinline="true"
type="application/x-mpegURL"
allowsInlineMediaPlayback="YES"
webview.allowsInlineMediaPlayback="YES"
width="100%"
ref="videoRef"
x5-video-player-fullscreen="true"
:poster="posterSrc"
@timeupdate="onTimeupdate"
>
<source id="sourceBox" :src="videoSrc" ref="sourceTV" />
<p class="vjs-no-js">不支持播放</p>
</video>
</div>
<div v-show="isError" class="errorTip">
<p>视频出错了!</p>
<br />
<p>{
{ textError }}</p>
</div>
<!-- 底部操作栏 -->
<div v-show="!isError" class="tv-footer-btn">
<div class="tv-btn tv-left">
<!-- 播放和暂停 -->
<div class="iconfont" v-if="tvBtnShow.stop">
<div
class="btn-image"
@click="TVboting"
:style="{
backgroundImage:
boting === true
? `url(${require('./video-img/pause.png')})`
: `url(${require('./video-img/pause-active.png')})`,
}"
></div>
</div>
<!-- 播放下一个视频 -->
<div class="iconfont" v-if="tvBtnShow.next">
<div
class="btn-image"
:style="{
backgroundImage:
nextBtn === true
? `url(${require('./video-img/next.png')})`
: `url(${require('./video-img/next-active.png')})`,
}"
></div>
</div>
<!-- 直播刷新 -->
<div class="iconfont" v-if="tvBtnShow.update" @click="TVupdate">
<div class="btn-image update-btn"></div>
</div>
</div>
<!-- 视频进度条 -->
<div class="tv-btn tv-centre">
<el-slider
class="time-progress-bar"
v-if="tvBtnShow.bar"
v-dragvideo
v-model="timeVal"
:show-tooltip="false"
@change="onTimeChange"
style="width: 100%"
>
</el-slider>
</div>
<div class="tv-btn tv-right">
<!-- 音量 -->
<div class="iconfont icon-muted" v-if="tvBtnShow.music">
<div
class="btn-image"
@click="TVsound"
:style="{
backgroundImage:
mutedIcon === true
? `url(${require('./video-img/muted.png')})`
: `url(${require('./video-img/muted-active.png')})`,
}"
></div>
<div class="volume-state">
<div class="box">
<el-slider
class="progress-bar"
v-model="volumeVal"
vertical
:show-tooltip="false"
@input="onMutedInput"
height="5vw"
>
</el-slider>
</div>
</div>
</div>
<!-- 全屏 -->
<div class="iconfont" v-if="tvBtnShow.full">
<div
class="btn-image"
@click="TVFullScreen"
:style="{
backgroundImage:
fullScreen === true
? `url(${require('./video-img/full-screen-active.png')})`
: `url(${require('./video-img/full-screen.png')})`,
}"
></div>
</div>
</div>
</div>
</div>
</template>
<script>
/* eslint-disable */
import videojs from "video.js";
import video_zhCN from "video.js/dist/lang/zh-CN.json";
import video_en from "video.js/dist/lang/en.json";
import "video.js/dist/video-js.css";
import "videojs-contrib-hls";
export default {
name: "videojs",
created() {
// 直播类型
const type = this.videoSrc
.substring(this.videoSrc.lastIndexOf(".") + 1)
.split("?")[0];
const t = type.toLowerCase();
if (t === "m3u8") {
this.tvBtnShow.update = true;
this.tvBtnShow.stop = false;
this.tvBtnShow.bar = false;
}
},
mounted() {
const that = this;
console.log("视频地址", this.videoSrc);
//为避免在初始化video时播放源是空的,报播放源错误,需要先给source 的src赋值
let player = videojs("videoTV", that.playOptions, function () {
// player.controls(false);
// this.play();
setTimeout(() => {
//延时确保能监听到视频源错误
let mediaError = this.error();
if (mediaError != null && mediaError.code) {
that.textError = mediaError.message;
console.log("播放失败", mediaError);
that.isError = true;
}
}, 1000);
this.on("loadedmetadata", function () {
// console.log("loadedmetadata---视频源数据加载完成----");
});
this.on("loadeddata", function () {
// console.log("loadeddata---渲染播放画面----");
});
this.on("timeupdate", function (event) {
// 监听视频播放进度,返回视频播放历史时间
that.$emit("historyTime", parseInt(this.currentTime()));
});
this.on("pause", function () {
that.boting = false;
});
this.on("play", function () {
that.boting = true;
});
this.on("fullscreenchange", function () {
that.fullScreen = this.isFullscreen_;
if (player.isFullscreen()) {
player.controls(true);
} else {
player.controls(false);
}
});
this.on("ended", function () {
that.boting = false;
console.log("ended---结束停止---");
that.isError = true;
});
});
videojs.addLanguage("zh-CN", video_zhCN);
videojs.addLanguage("en", video_en);
// player.on("click", function () {
// console.log("点击视频");
// }); //设置播放器宽度
},
beforeDestroy() {
const videoDom = this.$refs.videoRef; //不能用document 获取节点
videojs(videoDom).dispose(); //销毁video实例,避免出现节点不存在 但是flash一直在执行,也避免重新进入页面video未重新声明
},
directives: {
dragvideo: {
bind(el, binding, vnode, oldVnode) {
const els = el.querySelector(".el-slider__button-wrapper");
els.addEventListener("mousedown", (e) => {
const videoDom = videojs("videoTV");
videoDom.ready(function () {
let _this = this;
_this.pause();
});
els.addEventListener("mouseup", (e) => {
videoDom.ready(function () {
let _this = this;
_this.play();
});
});
});
},
},
},
props: {
videoSrc: {
type: String,
required: true,
},
posterSrc: {
type: String,
required: false,
},
},
data() {
return {
timeVal: 0, // 视频进度条值
mutedIcon: false, // 是否静音
volumeVal: 100, // 音量进度条值
boting: false, //是否暂停
nextBtn: true, // 切换下一个
fullScreen: false, //是否全屏
tvBtnShow: {
// 自定义操作按钮-Show
stop: true,
next: false,
update: false,
bar: true,
music: true,
full: true,
},
playOptions: {
autoplay: false, //自动播放
loop: true, //循环播放
muted: false, //是否静音
bigPlayButton: true,
textTrackDisplay: true,
posterImage: true,
errorDisplay: false,
playbackRates: false,
// controls: true, //是否拥有控制条
// controlBar: false, //关闭默认控制条
controlBar: {
// 设置控制条组件
/* 设置控制条里面组件的相关属性及显示与否
'currentTimeDisplay':true,
'timeDivider':true,
'durationDisplay':true,
'remainingTimeDisplay':false,
volumePanel: {
inline: false,
}
*/
/* 使用children的形式可以控制每一个控件的位置,以及显示与否 */
children: [
{ name: "playToggle" }, // 播放按钮
{ name: "liveDisplay" }, //直播流时,显示LIVE
// { name: "currentTimeDisplay" }, // 当前已播放时间
// { name: "progressControl" }, // 播放进度条
// { name: "durationDisplay" }, // 总时间
// {
// // 倍数播放
// name: "playbackRateMenuButton",
// playbackRates: [0.5, 1, 1.5, 2, 2.5],
// },
// {
// name: "volumePanel", // 音量控制
// inline: false, // 不使用水平方式
// },
{ name: "FullscreenToggle" }, // 全屏
],
},
},
textError: "",
isError: false,
};
},
methods: {
TVboting() {
//播放或暂停
let that = this;
const videoDom = videojs("videoTV");
videoDom.ready(function () {
let _this = this;
setTimeout(function () {
if (that.boting === true) {
_this.pause();
that.boting = false;
} else {
_this.play();
that.boting = true;
}
}, 20);
});
},
TVupdate() {
// 直播刷新
this.$emit("TVupdate");
},
onTimeupdate(event) {
// 调整声音进度条
this.timeVal = (event.target.currentTime / event.target.duration) * 100;
},
onTimeChange(e) {
// 声音进度条值,发生改变
const totalTime = document.querySelector("#videoTV video").duration; // 视频总时长
document.querySelector("#videoTV video").currentTime =
(this.timeVal / 100) * totalTime;
console.log("实际", (this.timeVal / 100) * totalTime);
},
onMutedInput(e) {
// 音量大小调整
document.querySelector("#videoTV video").volume = e / 100;
if (e !== 0) {
this.mutedIcon = false;
} else {
this.mutedIcon = true;
}
},
TVsound() {
// 是否静音
this.mutedIcon = !this.mutedIcon;
if (this.mutedIcon) {
this.volumeVal = 0;
document.querySelector("#videoTV video").volume = 0;
} else {
this.volumeVal = 100;
document.querySelector("#videoTV video").volume = 0.1;
}
},
TVFullScreen() {
// 全屏
let player = videojs("videoTV");
player.requestFullscreen();
player.isFullscreen(true);
},
TVsecond(time) {
// 视频播放历史位置的时间
document.querySelector("#videoTV video").currentTime = time;
},
},
};
</script>
<style>
/* 全屏操作栏样式 */
.live-video-wrapt .video-show .video-js .vjs-control-bar .vjs-volume-panel {
position: absolute;
right: 4em;
}
.live-video-wrapt
.video-show
.video-js
.vjs-control-bar
.vjs-fullscreen-control {
position: absolute;
right: 0;
}
/* 时间进度条 */
.live-video-wrapt .tv-footer-btn .tv-centre .el-slider .el-slider__runway {
background-color: rgba(0, 0, 0, 0.5);
margin: 0;
}
.live-video-wrapt
.tv-footer-btn
.tv-centre
.el-slider
.el-slider__runway
.el-slider__bar {
background-color: white;
}
.live-video-wrapt
.tv-footer-btn
.tv-centre
.el-slider
.el-slider__runway
.el-slider__button-wrapper
.el-tooltip.el-slider__button {
width: 0.5vw;
height: 0.5vw;
border: 2px solid white;
background-color: #de6f6f;
}
/* 声音进度条 */
.live-video-wrapt
.tv-footer-btn
.tv-btn
.iconfont
.volume-state
.box
.progress-bar
.el-slider__runway {
background-color: black;
}
.live-video-wrapt
.tv-footer-btn
.tv-btn
.iconfont
.volume-state
.box
.progress-bar
.el-slider__runway
.el-slider__bar {
background-color: white;
}
.live-video-wrapt
.tv-footer-btn
.tv-btn
.iconfont
.volume-state
.box
.progress-bar
.el-slider__runway
.el-slider__button-wrapper
.el-tooltip.el-slider__button {
width: 0.5vw;
height: 0.5vw;
border: 2px solid white;
background-color: #de6f6f;
}
</style>
<style scoped>
.live-video-wrapt {
width: 100%;
border-bottom: 1px solid #f0f0f0;
position: relative;
}
.video-show {
width: 100%;
position: relative;
}
.video-show .video-js.vjs-playing .vjs-tech {
pointer-events: auto;
}
.errorTip {
font-size: 2rem;
width: 100%;
height: 100%;
min-height: 8vw;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
}
/* 底部操作栏 */
.live-video-wrapt .tv-footer-btn {
width: 100%;
box-sizing: border-box;
padding: 1vw;
display: flex;
justify-content: space-evenly;
position: absolute;
left: 0;
bottom: 10%;
}
.live-video-wrapt .tv-footer-btn .tv-btn {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
.live-video-wrapt .tv-footer-btn .tv-btn .iconfont .btn-image {
width: 3vw;
height: 3vw;
background: no-repeat scroll center;
background-size: 3vw 3vw;
display: flex;
justify-content: center;
align-items: center;
}
.live-video-wrapt .tv-footer-btn .tv-btn .iconfont .update-btn {
background-image: url(./video-img/update.png);
}
.live-video-wrapt .tv-footer-btn .tv-btn .iconfont .update-btn:hover {
background-image: url(./video-img/update-hover.png);
}
/* 音量进度条 */
.live-video-wrapt .tv-footer-btn .tv-btn .icon-muted {
position: relative;
}
.live-video-wrapt .tv-footer-btn .tv-btn .icon-muted:hover .volume-state {
display: block;
}
.live-video-wrapt .tv-footer-btn .tv-btn .iconfont .volume-state {
width: 3vw;
height: 7.25vw;
display: none;
position: absolute;
left: 0;
bottom: 3vw;
}
.live-video-wrapt .tv-footer-btn .tv-btn .iconfont .volume-state .box {
width: 3vw;
height: 7vw;
border-radius: 0.25vw;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
/* */
.live-video-wrapt .tv-footer-btn .tv-centre {
width: 50%;
align-items: center;
}
.live-video-wrapt .tv-footer-btn .tv-left .iconfont {
margin-right: 1vw;
}
.live-video-wrapt .tv-footer-btn .tv-right .iconfont {
margin-left: 1vw;
}
</style>
直播
点播
文件夹结构
照片