示例2 - canvas绿幕效果

275 阅读1分钟

html:

  <body onload="processor.doLoad()">
    <div>
      <video id="video" src="./video/video.ogv" controls="true" />
    </div>
    <div>
      <!-- 显示原视频的当前帧 -->
      <canvas id="c1" width="160" height="96"></canvas>
      <!-- 显示绿幕替换后的图像 -->
      <canvas id="c2" width="160" height="96"></canvas>
    </div>
  </body>

css:

body {
  background: black;
  color: #cccccc;
}
#c2 {
  background-image: url('./video/bg1.jpg');
  background-repeat: no-repeat;
}
div {
  float: left;
  border: 1px solid #444444;
  padding: 10px;
  margin: 10px;
  background: #3b3b3b;
}

视频下载地址

js:

    let processor = {
      timerCallback: function () {
        if (this.video.paused || this.video.ended) {
          /* 判断视频是否处于正常播放状态 */
          return;
        }
        this.computeFrame();
        let self = this;
        setTimeout(function () {
          /* 递归调用,视频播放时循环执行 */
          self.timerCallback();
        }, 0);
      },

      doLoad: function () {
        /* 找到对应元素 */
        this.video = document.getElementById('video');
        this.c1 = document.getElementById('c1');
        this.ctx1 = this.c1.getContext('2d');
        this.c2 = document.getElementById('c2');
        this.ctx2 = this.c2.getContext('2d');
        let self = this;

        /* 点击播放时的回调(只在点击播放时调用一次) */
        this.video.addEventListener(
          'play',
          function () {
            console.log('play');
            self.width = self.video.videoWidth / 2;
            self.height = self.video.videoHeight / 2;
            self.timerCallback();
          },
          false
        );
      },

      computeFrame: function () {
        /* 将当前video播放的帧绘制到第一个canvas上 */
        this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
        /* 取得第一个canvas的当前图像 */
        let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
        /**
         * imageData.data.length用来读取像素数组的大小
         * 这里为什么要除以4:
         * 因为 frame.data 是一个包含了当前canvas的所有像素点颜色信息的一维数组。
         * 比如这里的canvas只有4个像素点,那么frame.data就会是这样的:
         * [
         *  212,
         *  213,
         *  24,
         *  0,
         *  212,
         *  213,
         *  24,
         *  0,
         *  212,
         *  213,
         *  24,
         *  0,
         *  213,
         *  214,
         *  25,
         *  0
         * ]
         * 数组的长度是4 * 4 ,每四个元素就是一个像素点的r,g,b,a值
         * 所以在这里除以4,得到的就是像素点的数量。在下面乘以4,得到的就是每个像素点的rgba的r的值。
         */
        let l = frame.data.length / 4;
        console.log('f----', frame.data);

        for (let i = 0; i < l; i++) {
          let r = frame.data[i * 4 + 0];
          let g = frame.data[i * 4 + 1];
          let b = frame.data[i * 4 + 2];
          /* 这里当当前像素点是黄色时,将这个点的透明度设置为0。
          * 需要注意的是:这里的alpha不是0-1,而是0-255,所以如果需要设置透明度为0.5,就不能写0.5,而是255 * 0.5
          */
          if (g > 100 && r > 100 && b < 43) frame.data[i * 4 + 3] = 0;
        }
        /* 除了人头像部分,video其他部分的透明度是0,因此人头像显示在背景图片上方 */
        /* 在场景内左上角绘制myImageData代表的图片 */
        this.ctx2.putImageData(frame, 0, 0);
        return;
      },
    };

最终效果:

image.png