美文网首页Webrtc
如何用 WebRTC 给自己拍照?

如何用 WebRTC 给自己拍照?

作者: 写代码的海怪 | 来源:发表于2022-02-26 13:55 被阅读0次

前言

哈喽,大家好,我是海怪。

最近一直在看 WebRTC 的用法,也学了一下音视频流的东西,今天就跟大家分享一个好玩的小实战吧——给自己拍照。

image

项目已上传至 Github,Repo
地址:https://github.com/haixiangyan/webrtc-take-photo

页面结构

首先,我们要拆分一下实现步骤:

  • 打开摄像头,获取视频流
  • 需要一个 <video> 来播放摄像头的画面
  • 点击按钮,生成画面,并展示在 <img>

因此,我们需要 <video>, <img> 以及 <button>

<head>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  
<main>
  <video id="video">浏览器不支持 Video</video>

  <canvas id="canvas">
    <img id="photo" alt="拍照后的照片">
  </canvas>
</main>

<div class="actions">
  <button id="takePhotoButton" type="button" class="btn btn-primary">拍照</button>
  <button id="downloadButton" type="button" class="btn btn-success">下载</button>
  <button id="clearButton" type="button" class="btn btn-danger">清空</button>
</div>

<script src="./main.js"></script>
  
</body>

再加上点 CSS,让整个 App 好看一点~

main {
    padding: 24px 24px 16px;
    display: flex;
}

video {
    margin-right: 16px;
    box-sizing: content-box;
    border: 4px solid #ffaabb;
}

canvas {
    box-sizing: content-box;
    border: 4px solid #aabbff;
}

.actions {
    padding: 0 24px;
}

.actions button {
    margin-right: 16px;
}
image

左边为摄像头的 <video>,右边则是拍照的图片。

打开摄像头

打开摄像头这一步,其实是调用了 WebRTC 的一个重要接口 navigator.mediaDevices.getUserMedia,通过这个接口不仅可以完成用户对摄像头的使用授权,还可以从返回值里直接拿到视频流:

const start = async () => {
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  takePhotoButton = document.getElementById('takePhotoButton');
  downloadButton = document.getElementById('downloadButton');

  // 获取摄像头的视频流
  try {
    video.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: false})
    video.play()
  } catch (e) {
    console.error(e)
  }
}

start().then()

将视频流接到 <video> 元素的 srcObject 上,再调用一下 video.play() 就可以展示视频流了:

image

拍照

从 HTML 结构里我们可以看到 <canvas> 里面还藏着一个 <img>。这里 <canvas> 的作用是负责从视频流中生成图片数据,再将这个数据放到 <img>src 上,这样就完成了我们的拍照功能了。

const takePhoto = () => {
  const context = canvas.getContext('2d')
  if (width && height) {
    // 将 video 元素的 width 和 height 拿过来
    canvas.width = width;
    canvas.height = height;

    context.drawImage(video, 0, 0, width, height);

    // 生成图片
    const data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  } else {
    clearPhoto()
  }
}

不过在调用 drawImage 的时候要传入对应的宽和高,这里的宽和高可以从 <video> 元素中的 widthheight 获得。

const start = async () => {
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  takePhotoButton = document.getElementById('takePhotoButton');
  downloadButton = document.getElementById('downloadButton');

  // 获取摄像头的视频流
  try {
    video.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: false})
    video.play()
  } catch (e) {
    console.error(e)
  }

  video.addEventListener('canplay', (event) => {
    if (!streaming) {
      // 按比例放大 videoHeight
      height = video.videoHeight / (video.videoWidth / width);

      // 设置 video 的宽高
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      // 设置 canvas 的宽高
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false)

  takePhotoButton.addEventListener('click', (event) => {
    // 拍照
    takePhoto()
  }, false)
}

start().then()

这样一来就能生成视频中定格后的某一个画面了。

image

从上面可以看到这里的 <img> 里的 srcdata:xxx 的图片数据。

清空图片

如果要重清除已经拍好的照片呢?我们可以利用 <canvas>fillRect 来生成一个空白图片,然后再转化成图片数据,放到 src 里就可以了:

// 清空操作
const clearPhoto = () => {
  const context = canvas.getContext('2d')
  // 生成空白图片
  context.fillStyle = "#AAA";
  context.fillRect(0, 0, canvas.width, canvas.height);
  const data = canvas.toDataURL('image/png');
  photo.setAttribute('src', data);
}

// 开始
const start = async () => {
  // ...

  clearButton.addEventListener('click', (event) => {
    clearPhoto();
  })

  // 生成默认空白图片
  clearPhoto();
}

start().then()

或许有的人会觉得:我把图片 v-if=falsedisplay: nonevisibility: hidden 不也可以嘛?

当然可以!这里我只是想再分享另一种思路嘛~ 因为像这种调用 fillRect 来做重置功能的是比较常用的,比较画板里的重置就可以这样来清空画布。

下载

下载则比较简单了,也是面试常考的一道技巧题。

先生成一个 <a> 标签,然后通过 <canvas> 生成 URL,将这个 URL 放到 href 里,用 JS 出发 click 事件,就可模拟下载了:

// 下载操作
const downloadPhoto = () => {
  const link = document.createElement('a');
  link.download = '你的帅照.png';
  link.href = canvas.toDataURL();
  link.click();
}

const start = async () => {
  // ...

  downloadButton.addEventListener('click', (event) => {
    // 下载
    downloadPhoto()
  })
}

start().then()

总结

到这里,这个小实战就结束啦,并没有什么难度,这里稍微做下总结吧。

WebRTC 最重要的 API 就是 await navigator.mediaDevices.getUserMedia({video: true, audio: false}),通过返回值可以获取当前摄像头、麦克风的音视频流。

通过 <video> 元素的 srcObject 属性可以直接接上视频流,这在直播、P2P、视频聊天的场景都可以这样使用。

通过 <canvas>drawImage 以及 fillRect 来生成视频图片以及空白图片数据,再把这些数据放到 <img> 就可以实现 JS 生成画面的效果。

如果你也喜欢我的文章,也可以关注我,你的三连是我最大的动力~

相关文章

  • 如何用 WebRTC 给自己拍照?

    前言 哈喽,大家好,我是海怪。 最近一直在看 WebRTC 的用法,也学了一下音视频流的东西,今天就跟大家分享一个...

  • 02.webRTC使用浏览器给自己拍照

    html client.js

  • 给自己拍照

    想从身体里再拔出一个自己,这样我站成风景,她拿着相机。 特别喜欢拍照,入了单反之后,天天四处找模特,碰到喜...

  • 你要这爱情有何用

    钱可以自己赚 耳环可以自己买 性可以自己给 你不会讲笑话 也不会做旅行攻略 更不会拍照 所以 你要这爱情,有何用?

  • iOS 音视频通话及会议2

    iOS webRTC + webSocket 实现音视频会议 webRTC在上一篇,已经给大家对webRTC做...

  • WebRTC 系列1--创建相机预览

    用 WebRTC 创建相机预览,不到 50 行核心代码就可以轻松搞定了。 WebRTC 依赖版本 直接使用官方给的...

  • 如何自己给宝宝拍照

    时值疫情期间,相信很多宝妈和我一样没能给刚出生没多久的宝贝拍得了新生儿照片或者满月照。本来懒惰的我想着就这么算了,...

  • WebRTC研究 (二) 实例demo

    @[TOC](WebRTC研究 (二) 实例demo) 1. WebRTC 简介 webrtc官网webrtc对i...

  • 生我何用?不能欢笑。灭我何用?不减狂骄。

    生我何用?不能欢笑。灭我何用?不减狂骄。 过自己想要的生活,上帝会让你付出代价。照顾好自己,爱自己才能爱好别人。如...

  • 入门

    WebRTC教程—概述[https://webrtc.org.cn/webrtc-tutorial-basic/]...

网友评论

    本文标题:如何用 WebRTC 给自己拍照?

    本文链接:https://www.haomeiwen.com/subject/zqpqrrtx.html