【vue】vue3-video.js组件封装

为播放 m3u8 地址需求封装的组件,参数选项如果不够参考官网属性修改
官网配置选项地址:https://videojs.com/guides/options/

RocVideojs.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<template>
<div class="roc-videojs">
<div v-if="reloadText" class="reload">
<img src="@/assets/images/loading.gif" alt="加载中" />
<p>{{ reloadText }}</p>
</div>
<video v-if="videoShow" class="video-js" ref="myPlayerRef" :poster="poster"></video>
</div>
</template>

<script setup name="RocVideojs">
import { nextTick, ref } from "vue";
import videojs from "video.js";
import zhCn from "video.js/dist/lang/zh-CN.json";
import "video.js/dist/video-js.min.css";

videojs.addLanguage("zh-CN", zhCn);

const props = defineProps({
src: {
type: String,
default: "",
},
type: {
type: String,
default: "application/x-mpegURL",
},
preload: {
type: String,
default: "none",
},
poster: {
type: String,
default: "",
},
controls: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: true,
},
width: {
type: [String, Number],
default: 320,
},
height: {
type: [String, Number],
default: 180,
},
});

const myPlayerRef = ref(null);
let myPlayer = null;
const videoShow = ref(false);
const reloadText = ref("");

/**
* 初始化播放器
*/
async function initVideojs() {
videoShow.value = true;
await nextTick();
const options = {
controls: props.controls,
autoplay: props.autoplay,
muted: true,
preload: props.preload,
width: props.width,
height: props.height,
sources: [
{
src: props.src,
type: props.type,
},
],
};
myPlayer = videojs(myPlayerRef.value, options, function () {
videojs.log("播放器准备好了");
if (props.autoplay) {
myPlayer.play();
}
myPlayer.on("error", () => {
reloadText.value = "视频出错,正在重连...";
myPlayer.load();
myPlayer.play();
});
myPlayer.on("playing", () => {
reloadText.value = "";
console.log("正在播放");
});
});
}

/**
* 播放
*/
function play() {
if (myPlayer) myPlayer.play();
}

/**
* 暂停播放
*/
function pause() {
if (myPlayer) myPlayer.pause();
}

/**
* 卸载播放器
*/
function dispose() {
if (myPlayer) myPlayer.dispose();
reloadText.value = "";
videoShow.value = false;
}

defineExpose({
init: initVideojs,
play,
pause,
dispose,
});
</script>

<style scoped lang="scss">
.roc-videojs,
.video-js,
video {
width: 100%;
height: 100%;
}
.roc-videojs {
position: relative;
.reload {
position: absolute;
left: 0;
top: 0;
z-index: 2;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 1);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
display: block;
width: 40px;
height: 40px;
}
p {
margin-top: 6px;
font-size: 20px;
}
}
}
</style>

使用:

如果在弹窗中并且播放的是推流地址类型(m3u8),关闭弹窗需要调用 dispose 方法销毁掉 videojs 对象,否则推流地址不会停;调用 dispose 方法后会发生打开第一次弹窗正常运行,点击第二次报错,是因为 dispose 方法会删除掉 video dom 元素,再次打开就会找不到 video 元素;在 RocVideojs 组件使用 v-if 处理每次打开弹窗都重新创建该组件 dom 即可(或者在 el-dialog 上加上 destroy-on-close 属性)。

cam.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<el-dialog :title="title" v-model="open" width="1200" append-to-body :close-on-click-modal="false" @closed="handleClosed">
<div style="width: 1120px; height: 630px;">
<RocVideojs v-if="open" ref="rocVideoRef" :poster="rowInfo.img_url" :src="rowInfo.video_url"></RocVideojs>
</div>
</el-dialog>
</template>

<script setup name="Cam">
import { ref, getCurrentInstance, nextTick } from "vue";
import RocVideojs from "@/components/RocVideojs";

const { proxy } = getCurrentInstance();

const title = ref("");
const open = ref(false);
const rowInfo = ref({});

/**
* 打开对话框
* @param row
*/
function handleOpen(row) {
rowInfo.value = row;
title.value = row.name;
open.value = true;
nextTick(() => {
proxy.$refs["rocVideoRef"].init();
});
}

/**
* 关闭对话框以后
*/
function handleClosed() {
proxy.$refs["rocVideoRef"].dispose();
}

defineExpose({ handleOpen });
</script>

<style scoped lang="scss"></style>