前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何用 WebRTC 给自己拍照?

如何用 WebRTC 给自己拍照?

作者头像
写代码的海怪
发布2022-03-29 16:48:39
9120
发布2022-03-29 16:48:39
举报
文章被收录于专栏:海怪的编程小屋

前言

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

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

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

页面结构

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

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

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

代码语言:javascript
复制
<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 好看一点~

代码语言:javascript
复制
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;
}

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

打开摄像头

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

代码语言:javascript
复制
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() 就可以展示视频流了:

拍照

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

代码语言:javascript
复制
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 获得。

代码语言:javascript
复制
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()

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

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

清空图片

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

代码语言:javascript
复制
// 清空操作
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 事件,就可模拟下载了:

代码语言:javascript
复制
// 下载操作
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 生成画面的效果。

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

参考资料

[1]

项目地址: https://github.com/haixiangyan/webrtc-take-photo

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 写代码的海怪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 页面结构
  • 打开摄像头
  • 拍照
  • 清空图片
  • 下载
  • 总结
    • 参考资料
    相关产品与服务
    云直播
    云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档