index.vue 6.9 KB
<template>
  <div ref="jPlayer" class="j-player"></div>
</template>

<script>
export default {
  name: 'JPlayer',
  props: {
    type: {
      type: String,
      default: 'live', // live playback
    },
    // 渠道ID
    channelId: {
      type: String,
      required: true,
    },
    datetimeRange: {
      type: Array,
      default: null,
    },
    watermark: {
      type: String,
      default: '',
    },
    // url 前缀
    prefixUrl: {
      type: String,
      default: 'static',
    },
    // TODO: 改变属性名称和ip地址由上层传入。若替换组件,应通知应用层人员
    playerAddress: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      // eslint-disable-next-line
      _iframeEl: null,
    }
  },
  computed: {
    prefixSegment() {
      if (this.prefixUrl) {
        return `/${this.prefixUrl}`;
      } else {
        return '';
      }
    },
    playerUrl() {
      if (this.channelId) {
        if (this.type === 'live') {
          // 直播
          return `${this.prefixSegment}/jplayer/index.html?ip=${this.playerAddress}&id=${this.channelId}`;
        } else if (this.type === 'playback') {
          // 回放
          if (this.datetimeRange && this.datetimeRange.length > 0) {
            const startTs = this.formatDatetime(this.datetimeRange[0]);
            const endTs = this.formatDatetime(this.datetimeRange[1] || (new Date().getTime()));
            // TODO: 时间戳
            return `${this.prefixSegment}/jplayer/playback.html?ip=${this.playerAddress}&id=${this.channelId}-${startTs}-${endTs}-1-0`;
            // return `/jplayer/playback.html?ip=${this.playerAddress}&id=${this.channelId}-1693877405-1693923756-1-0`;
          }
        }
      }

      return '';
    },
  },
  watch: {
    playerUrl(val) {
      console.log('playerUrl', val);
      console.log('playerUrl-iframe', this._iframeEl);
      if (val && this._iframeEl) {
        this._iframeEl.src = val;
      }
    },
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      // 获取playerURL,若地址存在则动态创建iframe
      if (this.playerUrl) {
        this.createPlayer(this.playerUrl);
      }
    },
    createPlayer(url) {
      const iframe = document.createElement('iframe');
      iframe.setAttribute('frameborder', 0);
      iframe.setAttribute('name', this.type);
      iframe.setAttribute('src', url);
      iframe.setAttribute('width', '100%');
      iframe.setAttribute('height', '100%');

      if (iframe.attachEvent) {
        iframe.attachEvent('onload', (event) => this.iframeReady(event));
      } else {
        iframe.onload = (event) => this.iframeReady(event);
      }

      // 插入iframe
      const commonIframeEl = this.$refs.jPlayer;
      if (commonIframeEl) {
        commonIframeEl.appendChild(iframe);
        this._iframeEl = iframe;
      }
    },
    iframeReady(event) {
      console.log('iframeReady', event.target);
      if (this.watermark && event.target) {
        const width = event.target.offsetWidth;
        const height = event.target.offsetHeight;
        this.createWatermark(width, height);
      }
    },
    createWatermark(width, height) {
      console.log('createWatermark', width, height);
      const canvas = this.createCanvas(width, height, this.watermark);
      const commonIframeEl = this.$refs.jPlayer;
      if (commonIframeEl) {
        commonIframeEl.appendChild(canvas);
      }
    },
    createCanvas(cwidth, cheight, watermark, image) {
      const canvas = document.createElement('canvas');
      canvas.width = cwidth;
      canvas.height = cheight - 40; // 40为底部工具条
      canvas.className = 'watermark';
      console.log('createCanvas', canvas);

      // 获取 Canvas 上下文
      const ctx = canvas.getContext('2d');

      // 在 Canvas 上绘制原始图片
      if (image) {
        ctx.drawImage(image, 0, 0);
      }

      // 绘制水印文本
      ctx.font = '24px Arial';
      ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';

      // 定义水印内容
      // TODO: 此部分应动态获取
      const watermarkText = watermark;

      // 定义水印间隔和边距
      const watermarkIntervalX = 300; // 水印之间的水平间隔
      const watermarkIntervalY = 300; // 水印之间的垂直间隔
      const watermarkMarginX = 50; // 水印距离左边的边距
      const watermarkMarginY = 180; // 水印距离顶部的边距

      // 循环绘制水印
      for (let x = watermarkMarginX; x < canvas.width; x += watermarkIntervalX) {
        for (let y = watermarkMarginY; y < canvas.height; y += watermarkIntervalY) {
          // 保存当前绘图状态以便恢复
          ctx.save();

          // 将坐标原点移动到水印位置
          ctx.translate(x, y);

          // 旋转文本
          ctx.rotate(-Math.PI / 4); // 旋转-45度(以弧度表示)

          // 绘制水印文本
          ctx.fillText(watermarkText, 0, 0);

          // 恢复绘图状态
          ctx.restore();
        }
      }

      return canvas;
    },
    formatDatetime(timestamp) {
      return parseInt(timestamp / 1000);
    },

    // 暴露外部
    screenshot() {
      const imgData = this._iframeEl.contentWindow.screenshotBase64();
      // console.log('handleScreenshot', imgData);

      return imgData;
    },
    screenshotWatermark() {
      // eslint-disable-next-line
      return new Promise((resolve, reject) => {
        // 创建一个新的图片对象
        const image = new Image();

        // 当图像加载完成后执行
        image.onload = () => {
          // 创建一个 Canvas 元素
          const canvas = this.createCanvas(image.width, image.height, this.watermark, image);

          // 将带有水印的 Canvas 转换回 base64
          const watermarkedBase64 = canvas.toDataURL('image/png');

          // 现在,watermarkedBase64 包含了带有水印的 base64 图像数据
          console.log(watermarkedBase64);
          resolve(watermarkedBase64);
        };
        image.error = function() {
          reject(new Error(null));
        }

        // 设置图像的 src 属性,加载图像
        image.src = this.screenshot();
      });
    },
    stopPlay() {
      return this._iframeEl.contentWindow.stopPlayer();
    },
    ptzController(arrow) {
      /**
       * arrow传参说明:
       * 方向控制:up, down, left, right, leftUp, rightUp, rightDown, leftDown
       * 缩放控制:zoomExpand, zoomNarrow
       * 调用完成,需要调用停止 ptzController('stop')
       *
       * 光圈控制:apertureFar, apertureNear
       * 聚焦控制:focusFar, focusNear
       * 调用完成,需要调用停止 ptzController('fiStop')
       */
      this._iframeEl.contentWindow.handlePtzController(arrow);
    },
  },
}
</script>

<style scoped lang="less">
.j-player {
  height: 100%;
  width: 100%;
  position: relative;
  /deep/canvas.watermark {
    position: absolute;
    top: 0px;
    left: 0px;
    // z-index: 10;
  }
}
</style>