关注
vue前端对接监控视频 hls格式

系列文章目录

vue前端对接监控视频 hls格式


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

接触智慧园区,智慧工地,水泵站等项目之后,发现都有实时监控的对接,并且是可以多屏进行,就研究了相关的技术栈,找到了这个强大的播放器对前端还是很友好的,话不多说上干货!!!


提示:代码片段里有博主对的接口,读代码的时候注意

一、准备工作

1. 使用npm 、cnpm、yarn 下载如下插件  
2. npm install xgplayer
3. npm install xgplayer-hls
下好以后在 package.json 检查一下

在这里插入图片描述

二、引入到index的文件当中

1.引入库、介绍

代码如下(示例):

import Player from "xgplayer"; // 这是他的一个方法
import FlvPlayer from "xgplayer-hls"; // 这个是搭配上面哪个使用的
import 'xgplayer/dist/index.min.css' // 这个是播放器的样式 ,可以在当前页面使用,也可以放全局

   let a = new Player({
      id: this.playList[0].id,// 对应<div :id='this.playList[0].id'></div> 
      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-input
          v-model="deptName"
          placeholder="请输入部门名称"
          clearable
          size="small"
          prefix-icon="el-icon-search"
          style="margin-bottom: 5px"
        />
      </div>
      <div class="treeDataLsit">
        <el-tree
          class="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
                    ? '/img/isOnlinetrue.png'
                    : '/img/isOnlinefalse.png'
                "
                alt=""
              />
              <span>{{ data.channelName }} </span>
            </div>
            <span v-else>{{ data.channelName }} </span>
          </span>
        </el-tree>
      </div>
      <div
        style="
          width: 100%;
          height: 42%;
          display: flex;
          flex-direction: column;
          position: relative;
        "
      >
        <div
          class="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">
            <div
              style="padding: 5px"
              @mousedown="BigCamera(1)"
              @mouseup="BigCameraMouseup(1)"
            >
              <img src="@/assets/jianTou/magnify.png" alt="" />
            </div>
            <div
              style="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">
            <div
              style="padding: 6px; font-size: 17px"
              :class="1 == 1 ? 'tableLIstStyle ' : 'table_hui'"
            >
              实时监控
            </div>
            <div
              style="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>
          <div
            class="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">
            <li
              v-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样式

<style lang="scss" scoped>
.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;
}
</style>

<style lang="scss">
.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;
}
</style>

三核心代码的书写

  1. video 划入事件
    // video 划入事件
    mouseenteBtn(value) {
      this.mouType = value;
      console.log(11111)
    },
  1. 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.msg
          window.open(aasd).download()
        })
      }
    },
  1. 分屏按钮 分别为 单屏 、四平、六平
  // 分屏按钮
    FenPing(value) {
    /*
    1.当你点击下方的三个图标的时候,分别获取 4 ,3 ,1三个值
    对应下面的三个判断
    2. 将对应的盒子添加上是shili:'' ,id(对应的上每一个盒子),当前视频的状态
    */
      this.IconFontNumber = value.number
      console.log(value.number)
      if (value.number == 4) {
        this.IconFontTypenumber = 2
        for (let index = 1; index < 4; index++) {
          this.playList.push({ ShiLi: null, id: `${'FenPingCenter_li' + index}`, type: 0, flg: false })
        }
      } else {
        this.IconFontTypenumber = 3
        for (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 = 1
        this.playList = this.playList.splice(0, 1)
        this.$forceUpdate()
      }
    },
  1. 每个分屏 的 实例销毁 按钮
    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;
    },
  1. 联合tree node-click事件
   handleNodeClick(value) {
      this.defaultPropsBtnBjc = value
      console.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 = player
            this.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 = 1
                this.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 = 1
                this.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 = 1
                  break;
                }
              }
            }
          }

        })
      }
    },
  1. 下面的代码是防止报错加的
    // 云台控制必须是球机-上下左右转动
    cameraBtn(value) {},
       // 停止事件xx`q
    mouseup(value) {},
     // 放大开始事件
    BigCamera(value) {},
    // 放大结束事件
    BigCameraMouseup(value) {}

转载自CSDN-专业IT技术社区

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/id_EquinoxFlower/article/details/132081571

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--