系列文章目录
vue前端对接监控视频 FLV格式
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
西瓜视频
- 系列文章目录
- 前言
- 一、准备工作
- 下好以后在 package.json 检查一下
- 二、引入到index的文件当中
- 1.引入库、介绍
- 2.开始绘制页面
- 定义的变量
- css样式
- 三核心代码的书写
- ` 到此也就结束,使用代码片段的时候需要将图片替换一下不然会出现路径的问题`
前言
接触智慧园区,智慧工地,水泵站等项目之后,发现都有实时监控的对接,并且是可以多屏进行,就研究了相关的技术栈,找到了这个强大的播放器对前端还是很友好的,话不多说上干货!!!
提示:代码片段里有博主对的接口,读代码的时候注意
一、准备工作
1. 使用npm 、cnpm、yarn 下载如下插件2. npm install xgplayer3. npm install xgplayer-flv4. npm install xgplayer-flv.js
下好以后在 package.json 检查一下
二、引入到index的文件当中
1.引入库、介绍
代码如下(示例):
import Player from "xgplayer"; // 这是他的一个方法import FlvPlayer from "xgplayer-flv"; // 这个是搭配上面哪个使用的import 'xgplayer/dist/index.min.css' // 这个是播放器的样式 ,可以在当前页面使用,也可以放全局 let a = new Player({id: this.playList[0].id,// 对应 isLive: true, // 是否是直播,plugins: [FlvPlayer], // 视频播放的方式url: '你的视屏地址', // 地址autoplay: true, // lang: "zh-cn", // 是否自动播放,autoplayMuted: true,//是否自动静音自动播放screenShot: true, //是否使用截图插件rotate:false,//是否使用旋转插件download:false,//是否使下载按钮pip:false,//是否使用画中画插件mini:false,//是否启用mini小窗插件playbackRate:[0.5, 0.75, 1, 1.5, 2],//倍速插件显示列表});
2.开始绘制页面
提示 :这里用到了Element-ui的tree组件,还有博主自己写一些样式就直接拿过来的 取自己用的即可
代码如下(示例):
<template><div class="list"><div class="lsieqqwe"><div class="boseList bgImg"><span@click="getall"style="color: #fff; margin-left: 12px; font-size: 16px">设备分组</span></div><div style="padding: 5px"><el-inputv-model="deptName"placeholder="请输入部门名称"clearablesize="small"prefix-icon="el-icon-search"style="margin-bottom: 5px"/></div><div class="treeDataLsit"><el-treeclass="filter-tree":data="treeData"ref="tree":props="defaultProps"node-key="id"default-expand-all:expand-on-click-node="false"highlight-current@node-click="handleNodeClick":filter-node-method="filterNode"><span slot-scope="{ node, data }" class="customize-tree-p"><div class="haha" v-if="node.level == 3"><img:src="data.isOnline == 1" />"alt=""/><span>{{ data.channelName }} </span></div><span v-else>{{ data.channelName }} </span></span></el-tree></div><divstyle="width: 100%;height: 42%;display: flex;flex-direction: column;position: relative;"><divclass="control toplst"v-if="zheDielist"@click="zheDielist = false"><div></div><div>云台控制</div><div><img src="@/assets/carmear/xiala.png" alt="" /></div></div><div v-if="zheDielist" style="height: 90%; flex: 1; margin-top: 65px"><div class="control_list"><img@mousedown="cameraBtn(1)"@mouseup="mouseup(1)"class="ShArrows"src="@/assets/jianTou/shang.png"alt=""/><img@mousedown="cameraBtn(2)"@mouseup="mouseup(2)"class="XArrows"src="@/assets/jianTou/xia.png"alt=""/><img@mousedown="cameraBtn(3)"@mouseup="mouseup(3)"class="ZArrows"src="@/assets/jianTou/zuo.png"alt=""/><img@mousedown="cameraBtn(4)"@mouseup="mouseup(4)"class="YArrows"src="@/assets/jianTou/you.png"alt=""/><img@mousedown="cameraBtn(7)"@mouseup="mouseup(7)"class="YSArrows"src="@/assets/jianTou/Yshang.png"alt=""/><img@mousedown="cameraBtn(8)"@mouseup="mouseup(8)"class="YXArrows"src="@/assets/jianTou/Yxia.png"alt=""/><img@mousedown="cameraBtn(6)"@mouseup="mouseup(6)"class="ZXArrows"src="@/assets/jianTou/Zxia.png"alt=""/><img@mousedown="cameraBtn(5)"@mouseup="mouseup(5)"class="ZSArrows"src="@/assets/jianTou/Zshang.png"alt=""/></div><div class="footerLsitStyle_why"><divstyle="padding: 5px"@mousedown="BigCamera(1)"@mouseup="BigCameraMouseup(1)"><img src="@/assets/jianTou/magnify.png" alt="" /></div><divstyle="padding: 5px; border-left: 2px solid #1f4075"@mousedown="BigCamera(2)"@mouseup="BigCameraMouseup(2)"><img src="@/assets/jianTou/reduce.png" alt="" /></div></div></div><div class="control footer" v-else @click="zheDielist = true"><div></div><div>云台控制</div><div><img src="@/assets/carmear/xiala.png" alt="" /></div></div></div></div><div style="width: 100%; padding: 10px"><div class="carStyle"><div class="shexinag"><div style="margin-left: 40px; display: flex"><divstyle="padding: 6px; font-size: 17px":class="1 == 1 ? 'tableLIstStyle ' : 'table_hui'">实时监控</div><divstyle="padding: 5px; font-size: 17px":class="1 == 2 ? 'tableLIstStyle' : 'table_hui'">录像回放</div></div></div></div><ul :class="1 != 1 ? 'FenPingCenter_box' : 'FenPingCenter_list_box'"><li:class="item == FenPingCenType ? 'FenPingCenter_Ui' : 'FenPingCenter_li'"v-for="(item, index) in IconFontNumber":key="item":style="{ width: 100 / IconFontTypenumber + '%' }"><div class="FenPingLise" v-if="item == mouType"><button @click="playerBtnItem(index)">关 闭</button></div><divclass="divVide":id="'FenPingCenter_li' + index"@mouseenter="mouseenteBtn(item)"@click="FenPingCenClick({ type: item, Nme: 'FenPingCenter_li' + index })"></div></li></ul><div class="footer_box"><div class="iconFlist_box"><span style="color: #fff">分屏 : </span><i@mousedown="FenPing(item)"v-for="item in IconFont":key="item.className":class="item.number == IconFontNumber? ` iconFlist${item.className}`: ` iconStyle ${item.className}`"></i></div><div><ul class="operationUl"><liv-for="(item, index) in operationArray":key="index"@click="FooterBtn(item)"><div><img :src="item.image" alt="" /></div><div>{{ item.name }}</div></li></ul></div></div></div></div></template>
定义的变量
data() {return {FenPingCenType: null, // mouType: null,destroyType: null,// 判断是否销毁zheDielist: false, // 左侧云台控制显示uuidv4: '',// uuid 视频放大时使用treeData: [ // 左侧 tree 的数据{id: 1,channelName: "全部",children: [],},],// 图标IconFontNumber: 1,IconFontTypenumber: 1,IconFont: [{className: 'iconfont icontiFenPingOne',number: 1,},{className: 'iconfont icontiFenPingSi',number: 4,},{className: 'iconfont icontiFenPingLiu',number: 6,},],operationArray: [ // 分屏数据// {// name: '抓拍',// image: require('../../../assets/carmear/zhuapai.png')// },// {// name: '放大',// image: require('../../../assets/carmear/BigList.png')// },// {// name: '录像',// image: require('../../../assets/carmear/luxiang.png')// },// {// name: '音频',// image: require('../../../assets/carmear/yinpin.png')// },// {// name: '对讲',// image: require('../../../assets/carmear/duijiang.png')// },// {// name: '全屏',// image: require('../../../assets/carmear/qaunping.png')// },],// tree 重置 数据的属性名defaultProps: {children: 'children',label: 'channelName'},// 部门名称deptName: undefined, // 用于筛选tree 数据selectArry: '',playList: [], // 所有视频的 实例cameraName: null, // 判断是否选中视频checkchannelId: null // 调取视频接口 接收其他页面传过来的数据};},
css样式
.list {width: 100%;padding: 20px;height: 90vh;display: flex;overflow-y: auto;background-color: #040c1e;.lsieqqwe {width: 20%;border: 1px solid #13355a;.boseList {padding: 5px;padding-left: 20px;font-size: 17px;}.bgImg {background-size: 100%, 100%;background-image: url("../../../assets/carmear/reight.png");}}.control {display: flex;justify-content: space-between;align-items: center;text-align: center;border-top: 2px solid #409eff;height: 10%;padding: 5px;color: #fff;background: linear-gradient(180deg, #246ab1 0%, #183a74 100%);img {width: 30px;}}.control_list {width: 100%;height: 210px;background-image: url("../../../assets/carmear/yuanpan.png");background-size: 100% 100%;background-repeat: no-repeat;position: relative;.ShArrows {position: absolute;top: 0;left: 0;margin-top: 9%;margin-left: 42%;}.XArrows {position: absolute;top: 0;left: 0;margin-top: 58%;margin-left: 42%;}.ZArrows {position: absolute;top: 0;left: 0;margin-top: 28%;margin-left: 17%;}.YArrows {position: absolute;top: 0;right: 0;margin-top: 28%;margin-right: 18%;}.YSArrows {position: absolute;top: 0;right: 0;margin-top: 15%;margin-right: 28%;}.YXArrows {position: absolute;top: 0;right: 0;margin-top: 50%;margin-right: 28%;}.ZXArrows {position: absolute;top: 0;left: 0;margin-top: 50%;margin-left: 28%;}.ZSArrows {position: absolute;top: 0;left: 0;margin-top: 16%;margin-left: 28%;}}.footerLsitStyle_why {padding: 5px;display: flex;justify-content: space-between;div {text-align: center;flex: 1;background-color: #183a74;img {width: 2rem;}}}.carStyle {width: 100%;height: 50px;.shexinag {width: 100%;height: 100%;display: flex;background-size: 100%, 100%;background-repeat: no-repeat;background-image: url("../../../assets/carmear/left.png");}.tableLIstStyle {color: #54c0ff;border-top: 3px solid #54c0ff;}.table_hui {border-top: 3px solid #54c0ff00;}}.footer_box {display: flex;padding: 10px;align-items: center;border: 1px solid #1d4a92;justify-content: space-between;.operationUl {display: flex;li {margin-right: 10px;text-align: center;}img {width: 30px;}}}.iconFlist_box {display: flex;font-size: 20px;.iconFlist {color: #239de7;font-size: 25px;margin-right: 15px;}.iconStyle {color: #6e809c;font-size: 25px;margin-right: 15px;}}}.footer {bottom: 0;right: 0;position: absolute;width: 100%;height: 45px;}.toplst {top: 0;left: 0;position: absolute;width: 100%;height: 45px;}.FenPingCenter_box {width: 100%;height: 80%;display: flex;flex-wrap: wrap;background-color: #ce9292;.FenPingCenter_li {border: 1px solid #25a9f6;background-color: #0c3781;position: relative;overflow: hidden;}.FenPingCenter_Ui {border: 2px solid #d18109;background-color: #0c3781;position: relative;overflow: hidden;}.FenPingLise {position: absolute;z-index: 99999;}}::v-deep.FenPingCenter_list_box:first-child {width: 66% !important;border: 10px solid red !important;}.FenPingCenter_list_box {height: 80%;display: flex;flex-wrap: wrap;background-color: #ce9292;.FenPingCenter_li {border: 1px solid #25a9f6;background-color: #0c3781;position: relative;overflow: hidden;}.FenPingCenter_Ui {border: 2px solid #d18109;background-color: #0c3781;position: relative;overflow: hidden;}.FenPingLise {position: absolute;z-index: 99999;}}// .leftbox {.treeDataLsit {height: 48%;// height: 78%;border: 1px solid #1e5494;overflow-y: auto;::v-deep .el-tree {color: rgba(255, 255, 255, 0.7);.el-tree-node__content:hover {background-color: #0c3781 !important;}&.el-tree--highlight-current.el-tree-node.is-current> .el-tree-node__content {background-color: #0c3781 !important;}}}.el-tree--highlight-current {background-color: #040c1e !important;}.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {background-color: #0c3781 !important;}.el-tree-node__content {background-color: red !important;}::v-deep .el-input--small .el-input__inner {background-color: #040c1e !important;border: none;}.divVide {width: 100% !important;position: relative !important;height: 100%;// border: 1px solid red;}::v-deep video {height: 50% !important;}.player-content {width: 100%;height: 10%;position: absolute;display: block;}// .iconqingxiLOGO {// display: none !important;// }#FenPingCenter_li0,#FenPingCenter_li1,#FenPingCenter_li2,#FenPingCenter_li3,#FenPingCenter_li4,#FenPingCenter_li5,#FenPingCenter_li6,#FenPingCenter_li7,#FenPingCenter_li8 {height: 100% !important;}// .xgplayer-fullscreen,.xgplayer-cssfullscreen,.xgplayer-volume,.icon-text {display: none !important;}
三核心代码的书写
- video 划入事件
// video 划入事件mouseenteBtn(value) {this.mouType = value;console.log(11111)},
- video 划出事件
// video 划入事件mouseout() {setTimeout(() => {this.mouType = null}, 1000)},
提示:视频出来后 显示 全屏或者 抓拍的 按钮 这个块不需要 如果是就一个平并且需要写自己的全屏抓拍样式的话可以用道 这个播放器本身带这些功能 只是样式不好看
FooterBtn(FooterObj) {console.log(FooterObj.name)if (FooterObj.name == '全屏') {// this.playList[this.FenPingCenType - 1].ShiLi.getFullscreen()} else if (FooterObj.name == '抓拍') {let aasd = ""// 这格式掉后端的接口xxxx({ id: this.defaultPropsBtnBjc.deviceCode }).then(res => {aasd = res.msgwindow.open(aasd).download()})}},
- 分屏按钮 分别为 单屏 、四平、六平
// 分屏按钮FenPing(value) {/*1.当你点击下方的三个图标的时候,分别获取 4 ,3 ,1三个值对应下面的三个判断2. 将对应的盒子添加上是shili:'' ,id(对应的上每一个盒子),当前视频的状态*/this.IconFontNumber = value.numberconsole.log(value.number)if (value.number == 4) {this.IconFontTypenumber = 2for (let index = 1; index < 4; index++) {this.playList.push({ ShiLi: null, id: `${'FenPingCenter_li' + index}`, type: 0, flg: false })}} else {this.IconFontTypenumber = 3for (let index = 1; index < 6; index++) {this.playList.push({ ShiLi: null, id: `${'FenPingCenter_li' + index}`, type: 0, flg: false })}}if (value.number == 1) {this.IconFontTypenumber = 1this.playList = this.playList.splice(0, 1)this.$forceUpdate()}},
- 每个分屏 的 实例销毁 按钮
playerBtnItem(index) {console.log(index); // 这个index是下标/**1.实例销毁调用.ShiLi.destroy();这个方法2.初始化当前的数据的状态*/this.mouType = null;this.destroyType = true;// // 视频的销毁this.playList[index].ShiLi.destroy();this.playList[index].ShiLi = null;this.playList[index].flg = true;this.playList[index].type = 0;},
- 联合tree node-click事件
handleNodeClick(value) {this.defaultPropsBtnBjc = valueconsole.log(value)let a = []/** * 判断是否是tree的最里层的数据 */if (!value.children) {this.playList.forEach(item => {if (item.type == 1) {a.push(item.type)}})// 接口xxxxx({ channelId: value.channelCode || this.checkchannelId }).then(res => {let b = a.length/** * 判断是否是 选中 容器 * 如果选择容器 * 判断他是否为null 查找到他的下标 * 拿容器ID 生成 播放器实例 * 并且插入到 实例的数据组中 * 将播放状态 改为 1 * */if (this.cameraName && !this.destroyType) {let player = new Player({id: this.cameraName,isLive: true,plugins: [FlvPlayer],url: res.data.url,autoplay: true,lang: "zh-cn",autoplayMuted: true,"screenShot": true});this.playList[this.FenPingCenType - 1].ShiLi = playerthis.playList[this.FenPingCenType - 1].type = 1} else {/** * 判断是否是是否是 销毁实例后 * 判断他是否为null 查找到他的下标 * 拿容器ID 生成 播放器实例 * 并且插入到 实例的数据组中 * 将播放状态 改为1 * */if (this.destroyType) {let iB = this.playList.findIndex(item => { return item.ShiLi == null })console.log(iB, 'ppppppp', this.FenPingCenType)if (this.FenPingCenType) {this.playList[this.FenPingCenType - 1].ShiLi = new Player({id: this.cameraName,isLive: true,plugins: [FlvPlayer],url: res.data.url,autoplay: true,lang: "zh-cn",autoplayMuted: true,"screenShot": true});this.playList[this.FenPingCenType - 1].type = 1this.destroyType = !this.playList.every(item => item.type == 1)// this.classLList()} else {this.playList[iB].ShiLi = new Player({id: this.playList[iB].id,isLive: true,plugins: [FlvPlayer],url: res.data.url,autoplay: true,lang: "zh-cn",autoplayMuted: true,"screenShot": true});this.playList[iB].type = 1this.destroyType = !this.playList.every(item => item.type == 1)}} else {for (let i = 0; i < this.playList.length; i++) {if (this.IconFontTypenumber == 1) {this.playList.forEach(item => {if (item.ShiLi != null) {item.ShiLi.destroy();item.ShiLi = null;item.type = 0;let a = new Player({id: this.playList[0].id,isLive: true,plugins: [FlvPlayer],url: res.data.url,autoplay: true,lang: "zh-cn",autoplayMuted: true,"screenShot": true,});item.ShiLi = a}return item})}console.log(9999)if (this.playList[i].ShiLi == null) {this.playList[b].ShiLi = new Player({id: this.playList[b].id,isLive: true,plugins: [FlvPlayer],url: res.data.url,autoplay: true,lang: "zh-cn",autoplayMuted: true,"screenShot": true,playsinline: true,height: window.innerHeight,width: window.innerWidth});this.playList[b].type = 1break;}}}}})}},
- 下面的代码是防止报错加的
// 云台控制必须是球机-上下左右转动cameraBtn(value) {}, // 停止事件xx`qmouseup(value) {}, // 放大开始事件BigCamera(value) {},// 放大结束事件BigCameraMouseup(value) {}