回放video 加 自定义tabBar 进度条

<template>
<!-- 回放 -->
  <div class='playback' ref="playback">
    <div class="video-wrapper">
      <video-player
        class="video-player vjs-custom-skin"
        ref="videoPlayer"
        :playsinline="true"
        crossOrigin="anonymous"
        style="height: 100%;"
        autoplay
        :options="playerOptions"
        @timeupdate="onPlayerTimeupdate($event)"
        @ended="onPlayerEnded($event)"
        @ready="playerReadied"
          @pause="onPlayerPause($event)"
             @playing="onPlayerPlaying($event)"
      >
        <span>1</span>
      </video-player>
      <div class="bottom-controls">
        <!-- 进度条 -->
        <div class="ruler-wrapper hide" ref="rulerWrapper">
          <div class="ruler-wrap">
            <div class="left" @click="swiperLeft"><i class="el-icon-arrow-left"></i></div>
            <div class="ruler-content-wrap">
              <div class="ruler-content-box">
                <div class="ruler-content" ref="content">
                  <div class="ruler-item" ref="item">
                    <div class="ruler-lists" v-for="(item, index) in 288" :key="index">
                      <span v-if="index%6===0">{{ _formatSeconds(index/6*30) }}</span>
                    </div>
                  </div>
                  <div class="drop1" v-for="(item, index) in drops" :key="'k1'+index" :style="[{width: item.width+'px'}, {left: item.position+'px'}]"></div>
                  <div class="drop" ref="drops" v-for="(item, index) in drops" :key="'k2'+index" :class="{'red': item.status}" :style="[{width: item.width+'px'}, {left: item.position+'px'}]" @click="videoClick($event, index)"></div>
                </div>
              </div>
              <div class="currTime" ref="currTime"></div>
            </div>
            <div class="right" @click="swiperRight"><i class="el-icon-arrow-right"></i></div>
          </div>
        </div>
        <!-- 底部控制菜单 -->
        <div class="control-wrapper">
          <!-- 左边 -->
          <div class="pic" @click="dianj">
            <el-image
              :url="dialogImageUrl.url"
              :src="dialogImageUrl.url"
              fit="cover"
            ></el-image>
          </div>
          <!-- 右边 -->
          <div class="bottomRight">
            <!-- 音量 -->
            <span class="urlRight">
              <el-image
                :url="songS.url"
                @click="yingliang"
                :src="songS.url"
                fit="cover"
              ></el-image>
              <el-slider
                class="pr"
                :show-tooltip="false"
                v-model="songsss"
                vertical
                height="100px"
                v-show="yl"
                @input="volumeChange"
              >
              </el-slider>
            </span>
            <!-- 截屏 -->
            <span download id="downLoadImg" class="urlRight">
              <el-image
                @click="getVideoPic"
                :url="jieP.url"
                :src="jieP.url"
                fit="cover"
              ></el-image>
            </span>
            <!-- 全屏 -->
            <span @click="fullScreenHandle" class="urlRight">
              <el-image
                :url="qScreen.url"
                :src="qScreen.url"
                fit="cover"
              ></el-image>
            </span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { videoPlayer } from "vue-video-player";
import "video.js/dist/video-js.css";

export default {
  components: {
    videoPlayer,
  },
  props: {
    domLength: { // 视频个数
      type: Number,
      default: 1
    },
    // devIds: { // 设备id
    //   type: String,
    //   default: "1"
    // },
    // dayDate: { //日期
    //   type: String,
    //   default: "2021-03-19"
    // },
    address: {
      type: Array
    }
  },
  data() {
    return {
         jieP: {
        url: require("@/assets/playback/jp.png"),
        flg: 1,
      },
      songS: {
        url: require("@/assets/playback/song.png"),
        flg: 1,
      },
      songS2: {
        url: require("@/assets/playback/song.png"),
        flg: 1,
      },
      songS1: {
        url: require("@/assets/playback/songN.png"),
        flg: 2,
      },
      qScreen: {
        url: require("@/assets/playback/qScreen.png"),
        flg: 1,
      },
      dialogImageUrl: {
        //播放按钮
        url: require("@/assets/playback/bf.png"),
        flg: 1,
      },
      dialogImageUrl1: {
        url: require("@/assets/playback/bf.png"),
        flg: 2,
      },
      dialogImageUrl2: {
        url: require("@/assets/playback/zd.png"),
        flg: 2,
      },
        songsss: 1,
       yl: false,
      currentVideo: 0,
      playerOptions: {
        controls: false, // 是否显示控制栏
        playbackRates: [0.5, 1.0, 1.5, 2.0], //播放速度
        autoplay: false, //如果true,浏览器准备好时开始回放。
        muted: false, // 默认情况下将会消除任何音频。
        loop: false, // 导致视频一结束就重新开始。
        preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
        language: "zh-CN",
        aspectRatio: "16:10", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
        sources: [
          {
            // type: "video/mp4",
            // src: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm", // url地址
            type: "application/x-mpegURL", // 这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目
            // src: "http://live.chci.cn/20210324/ANScHyUN3LcvGDPDuFbjiB/ANScHyUN3LcvGDPDuFbjiB.m3u8", //你的m3u8地址(必填)
          }
        ],
        // poster: "poster.jpg", //你的封面地址
        notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。

        controlBar: {
          timeDivider: true, //当前时间和持续时间的分隔符
          durationDisplay: true, //显示持续时间
          remainingTimeDisplay: true, //是否显示剩余时间功能
          fullscreenToggle: true, //全屏按钮
        }
      },
      videos: [
        // {
        //   id: 1,
        //   startDateStr: "00:05:00",
        //   endDateStr: "00:15:53",
        //   historyUrl: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm",
        //   status: 0
        // },
      ],
      drops: []
    }
  },
  created() {
    this.videos = this.address
    this.videoSwitch(true)
    this.resetControlStyle(this.domLength)
  },
  mounted() {
    
    this.domOnLoad()

    // const _this = this
    // window.onresize = function() {
    //   console.log(_this.$refs.videoPlayer.player.isFullscreen())
    // }
  },
  watch: {
    domLength(val) {
      this.resetControlStyle(val)
    },
    address(val) {
      this.videos = val
      this.videoSwitch(true)
    },
  },
  methods: {
    getVideoRecorders() {
      let params = {
        SSOID: this.$store.state.cookie,
        dayDate: this.dayDate,
        devIds: this.devIds
      }
      this.$axios
        .get("/apis/monitorcenter/device/getRecorders",  {
          params,
         
        }
         )
        .then((res) => {
        res.data.result[0].deviceRecorderList.map(res=>{
          console.log(
          res.historyUrl
          
          );
        })
          console.log(res);
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "视频加载失败 请重试!",
          });
        });
    },
    resetControlStyle(domLength) {
      let root = document.querySelector(":root")
      switch (domLength) {
        case 1:
          root.style.setProperty("--ruler-height", "100px")
          root.style.setProperty("--ruler-pad-top", "29px")
          root.style.setProperty("--ruler-big-line", "50px")
          root.style.setProperty("--ruler-small-line", "18px")
          root.style.setProperty("--ruler-font-size", "24px")
          root.style.setProperty("--ruler-font-top", "29px")
          root.style.setProperty("--ruler-swiper-size", "30px")
          root.style.setProperty("--ruler-currTime-top", "-49px")
          root.style.setProperty("--ruler-currTime-width", "40px")
          root.style.setProperty("--ruler-currTime-height", "163px")
          root.style.setProperty("--control-height", "90px")
          root.style.setProperty("--control-play-size", "50px")
          root.style.setProperty("--control-icon-size", "28px")
          break;
        case 4:
          root.style.setProperty("--ruler-height", "46px")
          root.style.setProperty("--ruler-pad-top", "13px")
          root.style.setProperty("--ruler-big-line", "24px")
          root.style.setProperty("--ruler-small-line", "8px")
          root.style.setProperty("--ruler-font-size", "12px")
          root.style.setProperty("--ruler-font-top", "13px")
          root.style.setProperty("--ruler-swiper-size", "16px")
          root.style.setProperty("--ruler-currTime-top", "-21px")
          root.style.setProperty("--ruler-currTime-width", "18px")
          root.style.setProperty("--ruler-currTime-height", "74px")
          root.style.setProperty("--control-height", "40px")
          root.style.setProperty("--control-play-size", "24px")
          root.style.setProperty("--control-icon-size", "12px")
          break;
        case 9:
          root.style.setProperty("--ruler-height", "30px")
          root.style.setProperty("--ruler-pad-top", "10px")
          root.style.setProperty("--ruler-big-line", "15px")
          root.style.setProperty("--ruler-small-line", "5px")
          root.style.setProperty("--ruler-font-size", "7px")
          root.style.setProperty("--ruler-font-top", "8px")
          root.style.setProperty("--ruler-swiper-size", "12px")
          root.style.setProperty("--ruler-currTime-top", "-14px")
          root.style.setProperty("--ruler-currTime-width", "12px")
          root.style.setProperty("--ruler-currTime-height", "49px")
          root.style.setProperty("--control-height", "26px")
          root.style.setProperty("--control-play-size", "16px")
          root.style.setProperty("--control-icon-size", "10px")
          break;
        case 16:
          root.style.setProperty("--ruler-height", "22px")
          root.style.setProperty("--ruler-pad-top", "7px")
          root.style.setProperty("--ruler-big-line", "12px")
          root.style.setProperty("--ruler-small-line", "4px")
          root.style.setProperty("--ruler-font-size", "4px")
          root.style.setProperty("--ruler-font-top", "6px")
          root.style.setProperty("--ruler-swiper-size", "10px")
          root.style.setProperty("--ruler-currTime-top", "-11px")
          root.style.setProperty("--ruler-currTime-width", "8px")
          root.style.setProperty("--ruler-currTime-height", "37px")
          root.style.setProperty("--control-height", "20px")
          root.style.setProperty("--control-play-size", "12px")
          root.style.setProperty("--control-icon-size", "8px")
          break
      }
    },
     // 截屏
    DPR() {
      // 获取设备dpi
      if (window.devicePixelRatio && window.devicePixelRatio > 1) {
        return window.devicePixelRatio;
      }
      return 1;
    },
    getVideoPic() {
      let video = document.getElementsByClassName("vjs-tech")[0];
      const scaleBy = this.DPR();
      let canvas = document.createElement("canvas");
      let w = window.innerWidth;
      let h = (window.innerWidth / 16) * 9;
      canvas.width = w * scaleBy;
      canvas.height = h * scaleBy;
      const ctx = canvas.getContext("2d");
      ctx.drawImage(video, 0, 0, w, h);
      ctx.drawImage(video, 0, 0, w, h);
      ctx.scale(scaleBy, scaleBy);
     
      let previewImg = canvas.toDataURL("image/png");
      //  console.log('1');

      this.down(previewImg);
    },
    async down(image) {
      //   下载图片
      let a = document.createElement("a"); // 创建一个a节点插入的document
      var event = new MouseEvent("click"); // 模拟鼠标click点击事件
      a.download = "downLoadImg"; // 设置a节点的download属性值
      a.href = image; // 将图片的src赋值给a节点的href
      a.dispatchEvent(event); // 触发鼠标点击事件
    },
    // 截屏结束
    // 点击
      dianj() {
      if (this.dialogImageUrl.flg == 2) {
        this.dialogImageUrl = this.dialogImageUrl1;
        this.dialogImageUrl.flg = 1;
        this.$refs.videoPlayer.player.pause();
      } else {
        this.dialogImageUrl = this.dialogImageUrl2;
        this.dialogImageUrl.flg = 2;
        this.$refs.videoPlayer.player.play();
      }
    },
     // 音量
    yingliang() {
      this.yl = !this.yl;
    },
    // 关闭声音
    volumeChange(val) {
      // console.log(va);
      this.$refs.videoPlayer.player.volume(val/100);
      // if (this.songsss == 0) {
      //   this.songS = this.songS1;
      // } else {
      //   this.songS = this.songS2;
      // }
      // console.log(this.songsss, "ddd");
    },
    swiperLeft() {
      this.$refs.content.scrollLeft -= Math.max(this.$refs.content.clientWidth, 0)
    },
    swiperRight() {
      this.$refs.content.scrollLeft += this.$refs.content.clientWidth

      // let left = parseInt(this.$refs.currTime.style.left)
      // let scrollLeft = this.$refs.content.scrollLeft
      // console.log(left)
      // this.$refs.currTime.style.left = left - scrollLeft + "px"
    },
    // 初始化多个视频位置
    domOnLoad() {
      let dateSecond = 24*60*60
      for(var i = 0;i < this.videos.length;i++) {
        let startTime = this._timeToSeconds(this.videos[i].startDateStr) // 开始
        let endTime = this._timeToSeconds(this.videos[i].endDateStr) // 结束
        let startPosi = startTime / dateSecond * this.$refs.item.clientWidth // 起始位置
        let videoWidth = (endTime - startTime) / dateSecond * this.$refs.item.clientWidth //视频宽度
        this.drops.push({
          position: startPosi,
          width: videoWidth,
          duration: endTime - startTime,
          status: this.videos[i].status
        })
        if(i===0) {
          this.$refs.content.scrollLeft = startPosi
        }
      }
    },
    videoClick(e, index) {
      let isSwitch = index !== this.currentVideo
      this.currentVideo = index
      let offsetX = e.offsetX
      let second = offsetX / this.drops[this.currentVideo].width * this.drops[this.currentVideo].duration
      this.videoSwitch(isSwitch, second)

      let contentWidth = this.$refs.content.getBoundingClientRect().left
      let scrollLeft = this.$refs.content.scrollLeft
      let currTimeLeft = e.pageX - contentWidth + scrollLeft
      
      // setTimeout(()=>{
      //   this.$refs.currTime.style.left = currTimeLeft+'px'
      // }, 200)
    },
    // 视频切换
    videoSwitch(switchVideo, time=0) {
      if(switchVideo) {
        this.playerOptions['sources'][0]['src'] = this.videos[this.currentVideo].historyUrl
        console.log(this.videos[this.currentVideo].historyUrl)
      }
      setTimeout(()=>{
        this.$refs.videoPlayer.player.currentTime(time)
        this.$refs.videoPlayer.player.play()
      }, 200)
    },
    // 视频播完回调
    onPlayerEnded($event) {
          this.dialogImageUrl = this.dialogImageUrl2;
      this.currentVideo += 1
      this.videoSwitch(true)
    },
      // 已开始播放回调
    onPlayerPlaying($event) {
      this.dialogImageUrl = this.dialogImageUrl2;
      console.log("开始前的回调", $event);
    },
    // 暂停回调

       onPlayerPause(player) {
      this.dialogImageUrl = this.dialogImageUrl1;
      console.log("暂停!", player);
    },
    // 当前播放位置发生变化时触发。
    onPlayerTimeupdate(player) {
      let currTime = player.cache_.currentTime
      let allTime = player.cache_.duration
      // let offsetX = currTime / allTime * this.drops[this.currentVideo].width // 指针偏移量
      let offsetX = currTime / allTime * this.drops[this.currentVideo].width // 指针偏移量
      let parentOffsetX = this.$refs.drops[this.currentVideo].offsetLeft // 盒子偏移量
      let scrollLeft = this.$refs.content.scrollLeft
      let allX = parentOffsetX + offsetX - scrollLeft

      this.$refs.currTime.style.left = allX + 'px'
      if(allX > this.$refs.content.clientWidth || (allX < 0 ? -allX > -this.$refs.content.clientWidth : false)) {
        // this.swiperRight()
        this.$refs.currTime.style.display = "none"
      }else {
        this.$refs.currTime.style.display = "block"
      }
    },
    //将侦听器绑定到组件的就绪状态。与事件监听器的不同之处在于,如果ready事件已经发生,它将立即触发该函数。。
    playerReadied(player, playtimes) {
      // console.log(playtimes);
      player.currentTime(playtimes)///开始的位置 可以通过这个值来修改
    },
    fullScreenHandle() {
      // console.log("全屏");
      let rulerWrapper = this.$refs.rulerWrapper
      let fullScreenBox = this.$refs.videoPlayer.player.el_
      let player = this.$refs.videoPlayer.player

    //   fullScreenBox.appendChild(rulerWrapper)
      
      if (!player.isFullscreen()) {
        player.requestFullscreen();
        player.isFullscreen(true);
      } else {
        player.exitFullscreen();
        player.isFullscreen(false);
      }
    },
    _formatSeconds(value) {
      let second = value % 60
      let minute = Math.floor(value/60) % 60
      return ('00'+minute).slice(minute.toString().length)+':'+('00'+second).slice(second.toString().length)
    },
    _timeToSeconds(value) {
      var s = '';
      var hour = value.split(':')[0];
      var min = value.split(':')[1];
      var sec = value.split(':')[2];
      s = Number(hour*3600) + Number(min*60) + Number(sec);
      return s;
    }
  }
}
</script>

<style scoped>
.playback, .video-wrapper {
  width: 100%;
  height: 100%;
}
.hide {
  opacity: 0;
}
.video-wrapper {
  position: relative;
}
.video-wrapper:hover .hide {
  opacity: 1;
}

.video-wrapper .bottom-controls {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  overflow-y: hidden;
  overflow-x: hidden;
  padding-top: 20px;
}
.video-wrapper .ruler-wrapper {
  width: 100%;
  background-color: rgba(0,0,0,0.6);
}
.ruler-wrap {
  width: 100%;
  height: 100px;
  font-size: 0;
  border: 1px solid #393b46;
  border-width: 1px 0;
}
.video-wrapper .left, .video-wrapper .right {
  display: inline-block;
  vertical-align: top;
  width: 5%;
  height: 100px;
  line-height: 100px;
  cursor: pointer;
  font-size: 27px;
  font-weight: bold;
  color: #ffffff;
  text-align: center;
}

.ruler-content-wrap {
  position: relative;
  display: inline-block;
  width: 90%;
}
.ruler-content-box {
  position: relative;
  height: 100px;
  overflow: hidden;
}
.ruler-content {
  position: relative;
  display: inline-block;
  width: 100%;
  height: 120px;
  overflow: hidden;
  overflow-x: scroll;
}
.ruler-item {
  font-size: 0;
  height: 100px;
  white-space: nowrap;
  position: absolute;
  z-index: 5;
}
.ruler-lists {
  display: inline-block;
  width: 35px;
  height: 100px;
  position: relative;
  vertical-align: top;
}
.ruler-lists::before, .ruler-lists:first-child::after {
  content: "";
  display: inline-block;
  width: 1px;
  height: 18px;
  background-color: #393b46;
  opacity: 0.5;
  position: absolute;
  right: 0;
  top: 0;
}
.ruler-lists:nth-child(6n)::before {
  height:  50px;
}
.ruler-lists:first-child::before {
  right: auto;
  left: 0;
  height: 50px;
}
.ruler-lists span {
  line-height: 31px;
  font-size: 24px;
  color: #eeeeee;
  vertical-align: top;
  position: absolute;
  left: 15px;
  top: 30px;
  z-index: 5;
}

.drop, .drop1 {
  height: 100px;
  position: absolute;
  top: 0;
}
.drop {
  z-index: 6;
}
.drop1 {
  background-color: #2e71b2;
}
.drop1.red {
  background-color: rgba(255,8,27,0.8);
}
.currTime {
  display: inline-block;
  width: 1px;
  height: 100px;
  position: absolute;
  top: 0;
  left: 0;
  transition: left .3s;
  z-index: 6;
}
.currTime::after {
  content: "";
  display: inline-block;
  width: 40px;
  height: 163px;
  background: url(../../../assets/playback/currTime@2x.png);
  background-size: cover;
  transform: translateX(-50%);
}
/* -------- */
.control-wrapper {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0;
  color: #fff;
  background-color: rgba(0,0,0,0.6);
}
.pic {
  padding: 5px;
  width: 30px;
  height: 30px;
  cursor: pointer;
}
.bottomRight {
  width: 15%;
  display: flex;
  justify-content: space-around;
}
.urlRight {
  z-index: 100;
  display: inline-block;
  width: 23px;
  height: 23px;
}
.urlRight .el-image {
  width: 100%;
  height: 100%;
  cursor: pointer;
  vertical-align: top;
}
.pr {
 
  position: absolute !important;
  top: -115px;
  left: 50%;
  transform: translateX(-50%);
}
</style>
<style lang="scss">
:root {
  --ruler-height: 100px;
  --ruler-pad-top: 29px;
  --ruler-big-line: 50px;
  --ruler-small-line: 18px;
  --ruler-font-size: 24px;
  --ruler-font-top: 29px;
  --ruler-swiper-size: 30px;
  --ruler-currTime-top: -49px;
  --ruler-currTime-width: 40px;
  --ruler-currTime-height: 163px;
  --control-height: 90px;
  --control-play-size: 50px;
  --control-icon-size: 28px;
}
/* 刻度尺样式兼容 */
.video-wrapper {
  .ruler-wrapper {
    bottom: var(--control-height);
    padding-top: var(--ruler-pad-top);
    .ruler-wrap, .ruler-content-box {
      height: var(--ruler-height);
    }
    .ruler-lists {
      height: var(--ruler-height);
      &::before {
        height: var(--ruler-small-line);
      }
      &:nth-child(6n)::before, &:first-child::before {
        height: var(--ruler-big-line);
      }
      span {
        top: var(--ruler-font-top);
        font-size: var(--ruler-font-size);
      }
    }
    .left, .right {
      height: var(--ruler-height);
      line-height: var(--ruler-height);
      font-size: var(--ruler-swiper-size);
    }
    .currTime {
      top: var(--ruler-currTime-top);
      &::after {
        width: var(--ruler-currTime-width);
        height: var(--ruler-currTime-height);
      }
    }
  }

  .control-wrapper {
    height: var(--control-height);
    line-height: var(--control-height);
    .pic {
      width: var(--control-play-size);
      height: var(--control-play-size);
      margin-left: 2.4%;
    }
    .urlRight {
        position: relative;
      width: var(--control-icon-size);
      height: var(--control-icon-size);
    }
  }
}
</style>
posted @ 2021-04-01 09:47  诡道也  阅读(186)  评论(0编辑  收藏  举报