使用video.js重新封装视频播放器支持点播及直播

一、安装指定版本是正常命令后加‘@版本号’

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>

直播

点播 

 

文件夹结构

照片

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/boundle_ss/article/details/121898473