likes
comments
collection
share

前端调取摄像头并实现拍照功能

作者站长头像
站长
· 阅读数 3

前言: 最近接到的一个需求十分有意思,设计整体实现了前端仿微信扫一扫的功能。整理了一下思路,做一个分享。

tips: 如果想要实现完整扫一扫的功能,你需要掌握一些前置知识,这次我们先讲如何实现拍照并且保存的功能。

一. window.navigator

  1. 你想调取手机的摄像头,首先你得先检验当前设备是否有摄像设备,window 身上自带了一个 navigator 属性,这个对象有一个叫做 mediaDevices 的属性是我们即将用到的。

  2. 于是我们就可以先设计一个叫做 checkCamera 的函数,用来在页面刚开始加载的时候执行。。 前端调取摄像头并实现拍照功能

  3. 我们先看一下这个对象有哪些方法,你也许会看到下面的场景,会发现这个属性身上只有一个值为 nullondevicechange 属性,不要怕,真正要用的方法其实在它的原型身上。 前端调取摄像头并实现拍照功能

  4. 让我们点开它的原型属性,注意下面这两个方法,这是我们本章节的主角。 前端调取摄像头并实现拍照功能

  5. 我们到这一步只是需要判断当前设备是否有摄像头,我们先调取 enumerateDevices 函数来查看当前媒体设备是否存在。它的返回值是一个 promise 类型,我们直接用 asyncawait 来简化一下代码。 前端调取摄像头并实现拍照功能 前端调取摄像头并实现拍照功能 从上图可以看出,我的电脑有两个音频设备和一个视频设备,那么我们就可以放下进行下一步了。

二. 获取摄像头

  1. 接下来就需要用到上面提到的第二个函数,navigator.getUserMedia。这个函数接收一个对象作为参数,这个对象可以预设一些值,来作为我们请求摄像头的一些参数。

  2. 这里我们的重点是 facingMode 这个属性,因为我们扫一扫一般都是后置摄像头 前端调取摄像头并实现拍照功能 当你执行了这个函数以后,你会看到浏览器有如下提示: 前端调取摄像头并实现拍照功能

  3. 于是你高兴的点击了允许,却发现页面没有任何变化。 前端调取摄像头并实现拍照功能

  4. 这里你需要知道,这个函数只是返回了一个媒体流信息给你,你可以这样简单理解刚刚我们干了什么,首先浏览器向手机申请我想用一下摄像头可以吗?在得到了你本人的确认以后,手机将摄像头的数据线递给了浏览器,:“诺,给你。”

  5. 浏览器现在仅仅拿到了一根数据线,然而浏览器不知道需要将这个摄像头渲染到哪里,它不可能自动帮你接上这根线,你需要自己找地方接上这根数据线。

  6. 这里不卖关子,我们需要请到我们的 Video 标签。我没听错吧?那个播放视频的 video 标签?没错,就是原生的 video 标签。

  7. 这里创建一个 video 标签,然后打上 ref 来获取这个元素。 前端调取摄像头并实现拍照功能

  8. 这里的关键点在于将流数据赋值给 video 标签的 srcObject 属性。就好像你拿到了数据线,插到了显示器上。 (tips: 这里需要特别注意,不是 video.src 而是 video.srcObject 请务必注意) 前端调取摄像头并实现拍照功能

  9. 现在你应该会看到摄像头已经在屏幕上展示了,这里是我用电脑前置摄像头录制的一段视频做成了gif。(脉动请给我打钱,哼) 前端调取摄像头并实现拍照功能

三. 截取当前画面

四. 源码

<script lang="ts" setup>
import { ref, onMounted } from "vue";

const wrapper = ref<HTMLDivElement>();
const videoEl = ref<HTMLVideoElement>();

async function checkCamera() {
  const navigator = window.navigator.mediaDevices;
  const devices = await navigator.enumerateDevices();
  if (devices) {
    const stream = await navigator.getUserMedia({
      audio: false,
      video: {
        width: 300,
        height: 300,
        // facingMode: { exact: "environment" }, //强制后置摄像头
        facingMode: "user", //前置摄像头
      },
    });
    if (!videoEl.value) return;

    videoEl.value.srcObject = stream;
    videoEl.value.play();
  }
}

function shoot() {
  if (!videoEl.value || !wrapper.value) return;
  const canvas = document.createElement("canvas");
  canvas.width = videoEl.value.videoWidth;
  canvas.height = videoEl.value.videoHeight;
  //拿到 canvas 上下文对象
  const ctx = canvas.getContext("2d");
  ctx?.drawImage(videoEl.value, 0, 0, canvas.width, canvas.height);
  wrapper.value.appendChild(canvas);
}

onMounted(() => {
  checkCamera();
});
</script>
<template>
  <div ref="wrapper" class="w-full h-full bg-red flex flex-col items-center">
    <video ref="videoEl" />
    <div
      @click="shoot"
      class="w-100px leading-100px text-center bg-black text-30px"
    >
      拍摄
    </div>
  </div>
</template>


五. 总结

实现拍照的整体思路其实很简单,仅仅需要了解到视频其实也是一帧一帧画面构成的,而 canvas 恰好有捕捉当前帧的能力。

预告:在下一篇会讲解如何实现扫一扫的功能,需要用到插件,感兴趣的同学可以先预习一下功课。🎁二维码扫码插件