video-player.vue 9 KB
<template>
  <div class="video-player">
    <video
      v-if="showVideo"
      ref="videoPlayer"
      class="video-js vjs-matrix vjs-yoho"
      :poster="coverImg"
      controls
      preload="auto"
      playsinline
      x5-playsinline
      webkit-playsinline="true">
      <source :src="source" :type="sourceType"></source>
      <p class="vjs-no-js">
        To view this video please enable JavaScript, and consider upgrading to a
        web browser
      </p>
      <div class="vjs-yoho-voice"></div>
    </video>
  </div>
</template>

<script>
import {get} from 'lodash';
import videojs from 'video.js';
import {getArticleImageSize, processImage} from 'utils/image-handler';
import versionCompare from 'utils/version';

export default {
  name: 'VideoPlayer',
  props: {
    source: String,
    cover: String,
    width: [Number, String],
    height: [Number, String],
    options: {
      type: Object,
      default() {
        return {
          muted: true,
          controls: true,
          aspectRatio: '1:1'
        };
      }
    }
  },
  data() {
    return {
      showVideo: false,
      player: null
    };
  },
  computed: {
    coverImg() {
      if (this.cover) {
        if (this.width && this.height) {
          let imgSize = getArticleImageSize({
            width: this.width,
            height: this.height,
            minScale: 0,
            maxWidth: 500
          });

          return processImage(this.cover, 2, imgSize.width, imgSize.height, get(this.yoho, 'window.supportWebp'));
        } else {
          return this.cover.split('?')[0];
        }
      } else {
        return '';
      }
    },
    sourceType() {
      let type = 'video/mp4';

      if (this.source) {
        let source = this.source.split('?')[0];

        source = source.split('.');

        switch (source[source.length - 1]) {
          case 'opus':
          case 'ogv':
            type = 'video/ogg';
            break;
          case 'mkv':
            type = 'video/x-matroska';
            break;
          case 'm3u8':
            type = 'application/x-mpegURL';
            break;
          case 'm4a':
            type = 'audio/mp4';
            break;
          case 'mp3':
            type = 'audio/mpeg';
            break;
          case 'aac':
            type = 'audio/aac';
            break;
          case 'oga':
            type = 'audio/ogg';
            break;
          default:
            break;
        }
      }

      return type;
    }
  },
  watch: {
    source() {
      this.showPlayer();
    }
  },
  mounted() {
    this.showPlayer();
  },
  beforeDestroy() {
    if (this.player) {
      this.player.dispose();
    }
  },
  methods: {
    showPlayer() {
      if (this.showVideo || !this.source) {
        return;
      }

      this.showVideo = true;
      this.$nextTick(() => {
        this.initPlayer();
      });
    },
    initPlayer() {
      const noVioceClass = 'vjs-yoho-novoice';

      this.player = videojs(this.$refs.videoPlayer, this.options);

      this.voiceBtn = this.player.addChild('button');
      this.voiceBtn.addClass('vjs-yoho-voice');
      this.voiceBtn.addClass(noVioceClass);
      this.backBtn = this.player.addChild('button');
      this.backBtn.addClass('vjs-yoho-back');
      this.voiceBtn.on('touchend', () => {
        this.player.muted(!this.voiceBtn.hasClass(noVioceClass));
      });
      this.backBtn.on('touchend', () => {
        this.player.exitFullscreen();
      });

      this.player.on('play', () => {
        this.player._yohoPlayTime = this.getTime();
      });
      this.player.on('pause', () => {
        this.player._yohoPauseTime = this.getTime();
      });
      this.player.on('ended', () => {
        this.player._yohoEndedTime = this.getTime();
      });

      this.player.on('fullscreenchange', () => {
        // ios 退出全屏自动播放
        if (this.$yoho.isiOS && versionCompare(this.$yoho.appVersion, '6.9.7') > 0) {
          let changeTime = this.getTime();

          let playTime = this.player._yohoPlayTime || 0;

          let pauseTime = this.player._yohoPauseTime || 0;

          let endedTime = this.player._yohoEndedTime || 0;

          if ((changeTime - pauseTime) < 600 && pauseTime > playTime && playTime > endedTime) {
            this.player.play();
          }
        }
      });

      this.player.on('volumechange', () => {
        const soundOff = this.player.muted() || this.player.volume() === 0;

        if (soundOff) {
          this.voiceBtn.addClass(noVioceClass);
        } else {
          this.voiceBtn.removeClass(noVioceClass);
        }
      });
    },
    getTime() {
      return new Date().getTime();
    }
  }
};
</script>

<style lang="scss" scoped>
  .video-js {
    width: 100%;
    height: auto;

    video {
      width: 100%;
    }

    &.vjs-yoho /deep/ {
      position: relative;
      background-color: #222;

      .vjs-resize-manager {
        z-index: 0;
        visibility: hidden;
      }

      .vjs-poster {
        background-color: #222;
      }

      .vjs-yoho-voice {
        width: 60px;
        height: 60px;
        position: absolute;
        top: 20px;
        right: 28px;
        opacity: 0;
        visibility: hidden;
        background: url("~statics/image/components/video-voice-icon.png");
        background-size: cover;
        background-repeat: no-repeat;

        &.vjs-yoho-novoice {
          background-position: bottom left;
        }
      }

      &.vjs-has-started .vjs-yoho-voice {
        visibility: visible;
        opacity: 1;
        transition: visibility 1s, opacity 1s;
      }

      &.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-yoho-voice {
        visibility: visible;
        opacity: 0;
      }

      .vjs-big-play-button {
        width: 126px;
        height: 126px;
        line-height: 126px;
        font-size: 60px;
        border: none;
        left: 50%;
        top: 50%;
        margin: -63px auto auto -63px;
        background: url("~statics/image/components/video-play-btn.png");
        background-size: contain;
        background-repeat: no-repeat;

        > * {
          display: none;
        }
      }

      @keyframes roundframe {
        0% { -webkit-transform: rotate(0deg); }
        25% { -webkit-transform: rotate(90deg); }
        50% { -webkit-transform: rotate(180deg); }
        75% { -webkit-transform: rotate(270deg); }
        100% { -webkit-transform: rotate(360deg); }
      }

      .vjs-loading-spinner {
        width: 110px;
        height: 110px;
        margin-top: -55px;
        margin-left: -55px;
        border: none;
        background: url("~statics/image/components/video-loading-icon.png");
        background-size: contain;
        background-repeat: no-repeat;
        background-position: center center;
        visibility: visible;
        animation: roundframe 1.3s linear infinite;

        &:before,
        &:after,
        > * {
          display: none;
        }
      }

      .vjs-control-bar {
        height: 80px;
        background: none;
        padding-bottom: 20px;

        .vjs-control {
          width: 80px;
        }

        .vjs-play-control {
          width: 60px;
          height: 60px;
          margin-left: 20px;
          background: url("~statics/image/components/video-play-icon.png");
          background-size: cover;

          &.vjs-playing {
            background-position: bottom left;
          }

          > * {
            display: none;
          }
        }

        .vjs-volume-panel {
          display: none;
        }

        .vjs-duration,
        .vjs-current-time {
          display: block;
          order: 3;

          > span {
            font-size: 22px;
            font-weight: 300;
          }
        }

        .vjs-time-control {
          line-height: 60px;
        }

        .vjs-current-time {
          order: 1;
        }

        .vjs-remaining-time {
          display: none;
        }

        .vjs-progress-control {
          order: 2;

          .vjs-progress-holder {
            margin: 0;
          }

          .vjs-progress-holder,
          .vjs-load-progress,
          .vjs-play-progress {
            height: 1px;
          }

          .vjs-play-progress:before {
            font-size: 10px;
            top: 0;
            transform: translateY(-44%);
          }
        }

        .vjs-fullscreen-control {
          order: 4;
          width: 60px;
          height: 60px;
          margin-right: 20px;
          background: url("~statics/image/components/video-fullscreen-icon.png");
          background-size: cover;

          > * {
            display: none;
          }
        }

        .vjs-icon-placeholder:before {
          font-size: 38px;
          line-height: 60px;
        }
      }

      &.vjs-fullscreen .vjs-fullscreen-control {
        background-position: bottom left;
      }

      &.vjs-fullscreen .vjs-yoho-back {
        width: 40px;
        height: 42px;
        background: url("~statics/image/components/video-back-icon.png");
        background-size: contain;
        background-repeat: no-repeat;
        position: absolute;
        top: 20px;
        left: 32px;
      }
    }
  }
</style>