前端实现视频重叠,抠图效果

方法有两种

一、 使用透明背景的webm格式视频

具体方式可自行百度,跟代码关系不大,直接用就行,但是支持程度可能不太行

二、使用canvas进行抠图

mdn链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Manipulating_video_using_canvas
html部分
<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <style>
    body {
      color: #CCCCCC;
    }

    #c2 {
      /* background-image: url("/assets/images/blue.png"); */
      background-size: cover;
      background-repeat: no-repeat;
    }

    div {
      float: left;
      border: 1px solid #444444;
      padding: 10px;
      margin: 10px;
    }

    .d-flex {
      display: flex;
    }

    .main {
      width: 500px;
    }

    video {
      width: 100%;
    }

    canvas {
      margin-top: 20px;
      width: 450px;
    }

    .right {
      width: 450px;
      position: relative;
      overflow-x: hidden;
    }

    .center {
      width: 100%;
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      object-fit: cover;
      top: 0;
      pointer-events: none;
      z-index: -1;
    }

    .text-white {
      color: #fff;
    }
  </style>
  <script type="text/javascript;" src="main.js"></script>
</head>

<body class="d-flex">
  <div class="main">
  	<!--需要抠图的视频-->
    <video preload="auto" id="video" loop src="./assets/video/out.webm" controls="true" />
  </div>
  <canvas id="c1" width="1080" height="1920">
  </canvas>
  <div class="right">
  	<!--背景视频-->
    <video preload="auto" class="center" id="personVideo" muted autoplay src="./assets/video/bg.WebM"></video>
    <!--输出视频-->
    <canvas id="c2" width="1080" height="1920" />
  </div>
</body>

注意素材自己找一下
JS部分

/*
 * @Description: 
 * @Date: 2021-08-19 14:49:56
 * @LastEditTime: 2021-08-20 10:53:02
 * @FilePath: \virtualGuide(6)\src\util\videoHandler.js
 */
export class VideoHandler {
  // 源视频
  video = null
  width = 1080
  height = 1920
  // canvas1, 用于复制视频
  c1 = null
  ctx1 = null
  // canvas2, 输出去除背景之后的视频
  c2 = null
  ctx2 = null
  /**
   * @description: 构造方法
   * @param {HTMLVideoElement} source 原视频
   * @param {HTMLCanvasElement} canvas 输出canvas
   * @param {?HTMLCanvasElement} c1 绘制源视频的canvas,不穿默认创建
   * @return {*}
   */
  constructor(source, canvas, c1, width = 1080, height = 1920) {
    try {
      this.video = source
      if (c1) {
        this.c1 = c1
      } else {
        this.c1 = document.createElement("canvas")
        this.c1.width = this.width
        this.c1.height = this.height
      }
      this.ctx1 = this.c1.getContext('2d')
      this.c2 = canvas
      this.ctx2 = canvas.getContext('2d')
      this.width = width;
      this.height = height;
    } catch (error) {
      console.warn(error)
    }
  }

  /**
   * @description: 初始化dom,添加监听视频的事件
   * @param {*}
   * @return {*}
   */
  doLoad() {
    this.video?.addEventListener('play', () => {
      this.timerCallback();
    }, false);
  }

  /**
   * @description: 当视频播放时,递归绘制视频,这里的延时可根据需求自行设置,16为每秒绘制60多次,基本满足视觉要求
   * @param {*}
   * @return {*}
   */
  timerCallback() {
    if (this.video.paused || this.video.ended) {
      return;
    }
    this.computeFrame();
    let self = this;
    setTimeout(function () {
      self.timerCallback();
    }, 16);
  }

  /**
   * @description: 绘制每一帧视频
   * @param {*}
   * @return {*}
   */
  computeFrame() {
    this.ctx1.clearRect(0, 0, this.width, this.height)
    this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);

    // 每四个为一个像素点,分别为rgba
    let step = 4

    // 像素点个数
    let l = frame.data.length / step;
    for (let i = 0; i < l; i++) {
      let r = frame.data[i * step + 0];
      let g = frame.data[i * step + 1];
      let b = frame.data[i * step + 2];
      // rgba通道,当颜色为黑色时,a通道设置为0
      if (g < 10 && r < 10 && b < 10)
        frame.data[i * step + 3] = 0;

    }
    this.ctx2?.putImageData(frame, 0, 0);
    return;
  }

  /**
   * @description: 绘制第一帧,需要源视频设置preload:auto
   * @param {*}
   * @return {*}
   */
  printFirstFrame() {
    this.video.currentTime = 0
    console.log(this.video.currentTime, "first");
    setTimeout(() => {
      this.ctx1.clearRect(0, 0, this.width, this.height)
      this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
      let frame = this.ctx1.getImageData(0, 0, this.width, this.height);

      // 每四个为一个像素点,分别为rgba
      let step = 4

      // 像素点个数
      let l = frame.data.length / step;
      for (let i = 0; i < l; i++) {
        let r = frame.data[i * step + 0];
        let g = frame.data[i * step + 1];
        let b = frame.data[i * step + 2];
        // rgba通道,当颜色为黑色时,a通道设置为0
        if (g < 10 && r < 10 && b < 10)
          frame.data[i * step + 3] = 0;

      }
      // console.log(this, this.ctx2);
      this.ctx2?.putImageData(frame, 0, 0);
    }, 20);

  }
}

使用

      let video = document.querySelector("#personVideo");
      let canvas = document.querySelector("#c2");
      const videoHandler = new VideoHandler(video, canvas);
      const videoHandler.doLoad();

视频不方便发布,可以看下mdn的效果图

总结

第一种方法方便快捷,就是webm格式的透明背景视频不好弄,可以让ui大哥帮忙。但是适配性可能也不太好
第二种方法基本上只要支持canvas的浏览器都能用,但是会把背景里所有目标颜色都设为透明,比如我背景色是黑色,我要扣的图里面如果有黑色也会被扣掉,例如头发眼睛。当然这种可以通过改背景色来解决;也可以优化处理像素的算法来解决

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值