电脑端调用摄像头拍照:从基础到实现

文章目录

    • 1. 了解`navigator.mediaDevices.getUserMedia` API
    • 2. 创建 HTML 结构
    • 3. 编写 JavaScript 代码
      • 3.1 打开摄像头
      • 3.2 拍照
    • 4. 完整代码
    • 5. 测试
    • 6. 注意事项及部署

在现代 Web 开发中,调用摄像头进行拍照是一个常见的功能,尤其是在需要用户上传头像、进行身份验证或实时交互的场景中。本文将逐步介绍如何在电脑端通过 HTML 和 JavaScript 调用摄像头进行拍照,并上传照片。我们将使用navigator.mediaDevices.getUserMedia API 来实现这一功能。

1. 了解navigator.mediaDevices.getUserMedia API

  • MediaDevices.getUserMedia() API

  • navigator.mediaDevices.getUserMedia 是一个现代浏览器提供的 API,用于访问用户的摄像头和麦克风。MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其他轨道类型。它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promise 会reject回调一个 PermissionDeniedError 或者 NotFoundError

  • 通过这个 API,我们可以请求设备的媒体输入,并将其流式传输到 HTML 的 <video> 元素中。

主要步骤

  1. 请求用户权限:在使用摄像头之前,必须请求用户的权限。
  2. 获取媒体流:通过navigator.mediaDevices.getUserMedia获取媒体流。
  3. 显示视频流:将媒体流绑定到 <video> 元素中。
  4. 拍照:使用 <canvas> 元素捕获视频帧并生成图片。

2. 创建 HTML 结构

首先,我们需要创建一个简单的 HTML 页面,包含一个视频元素、一个画布元素和两个按钮(一个用于打开摄像头,一个用于拍照)。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>摄像头拍照</title>
    <style>
      .btn {
        display: flex;
        width: 40%;
        height: 80px;
        align-items: center;
        justify-content: space-around;
      }
      .btn div {
        background-color: purple;
        color: white;
        border-radius: 10px;
        padding: 1rem;
      }
    </style>
  </head>
  <body>
    <div></div>
    <video id="video" autoplay style="width: 200px; height: 200px"></video>
    <canvas
      id="canvas"
      width="200"
      height="200"
      style="border: 5px dotted yellowgreen"
    ></canvas>
    <div class="btn">
      <div id="open">打开摄像头</div>
      <div id="photo">拍照</div>
    </div>
  </body>
</html>

3. 编写 JavaScript 代码

3.1 打开摄像头

当点击 打开摄像头 按钮时,我们需要调用navigator.mediaDevices.getUserMedia来请求摄像头权限,并将视频流绑定到 <video> 元素中。

let open = document.querySelector("#open");
let video = document.querySelector("#video");

open.onclick = () => {
  let constraints = {
    video: { width: 200, height: 200 },
    audio: false,
  };

  navigator.mediaDevices
    .getUserMedia(constraints)
    .then((stream) => {
      video.srcObject = stream;
      video.play();
    })
    .catch((error) => {
      console.error("摄像头打开失败:", error);
    });
};

3.2 拍照

当点击拍照 按钮时,我们需要使用 <canvas> 元素捕获当前视频帧,并将其转换为图片。

let photo = document.querySelector("#photo");
let canvas = document.querySelector("#canvas");

photo.onclick = () => {
  let ctx = canvas.getContext("2d");
  ctx.drawImage(video, 0, 0, 200, 200);

  // 将画布内容转换为 Base64 格式
  let url = canvas.toDataURL("image/png");
  console.log("Base64 URL: ", url);

  // 将画布内容转换为 Blob 对象
  canvas.toBlob((blob) => {
    let blobUrl = URL.createObjectURL(blob);
    let file = new window.File([blob], "封面图.png", { type: "image/png" });
    console.log("Blob URL: ", blobUrl);
    console.log("创建的新文件:", file);

    // 使用FormData上传图片
    let formData = new FormData();
    formData.append("img", file);

    fetch("http://localhost:8000", {
      method: "post",
      body: formData,
    })
      .then((response) => {
        if (response.ok) {
          console.log("文件上传成功");
        } else {
          console.error("文件上传失败");
        }
      })
      .catch((error) => {
        console.error("图片上传失败:", error);
      });
  });
};

4. 完整代码

将上述 HTML 和 JavaScript 代码组合在一起,完整的 HTML 文件如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>摄像头拍照</title>
    <style>
      .btn {
        display: flex;
        width: 40%;
        height: 80px;
        align-items: center;
        justify-content: space-around;
      }
      .btn div {
        background-color: purple;
        color: white;
        border-radius: 10px;
        padding: 1rem;
      }
    </style>
  </head>
  <body>
    <div></div>
    <video id="video" autoplay style="width: 200px; height: 200px"></video>
    <canvas
      id="canvas"
      width="200"
      height="200"
      style="border: 5px dotted yellowgreen"
    ></canvas>
    <div class="btn">
      <div id="open">打开摄像头</div>
      <div id="photo">拍照</div>
    </div>
    <script>
      let open = document.querySelector("#open");
      let video = document.querySelector("#video");
      let photo = document.querySelector("#photo");
      let canvas = document.querySelector("#canvas");

      open.onclick = () => {
        let constraints = {
          video: { width: 200, height: 200 },
          audio: false,
        };

        navigator.mediaDevices
          .getUserMedia(constraints)
          .then((stream) => {
            video.srcObject = stream;
            video.play();
          })
          .catch((error) => {
            console.error("摄像头打开失败:", error);
          });
      };

      photo.onclick = () => {
        let ctx = canvas.getContext("2d");
        ctx.drawImage(video, 0, 0, 200, 200);

        // 将画布内容转换为Base64格式
        let url = canvas.toDataURL("image/png");
        console.log("Base64 URL: ", url);

        // 将画布内容转换为Blob对象
        canvas.toBlob((blob) => {
          let blobUrl = URL.createObjectURL(blob);
          let file = new window.File([blob], "封面图.png", {
            type: "image/png",
          });
          console.log("Blob URL: ", blobUrl);
          console.log("创建的新文件:", file);

          // 使用FormData上传图片
          let formData = new FormData();
          formData.append("img", file);

          fetch("http://localhost:8000", {
            method: "post",
            body: formData,
          })
            .then((response) => {
              if (response.ok) {
                console.log("文件上传成功");
              } else {
                console.error("文件上传失败");
              }
            })
            .catch((error) => {
              console.error("图片上传失败:", error);
            });
        });
      };
    </script>
  </body>
</html>

5. 测试

测试:在本地运行 HTML 文件,使用 live Server,确保摄像头可以正常打开,并且拍照功能可以正常工作。

截图
FormData
在这里插入图片描述

6. 注意事项及部署

  • 部署:将 HTML 文件部署到支持 HTTPS 的服务器上,因为navigator.mediaDevices.getUserMedia需要在 HTTPS 环境下工作。
  • 浏览器支持:确保目标浏览器支持navigator.mediaDevices.getUserMedia API。
  • 错误处理:在实际应用中,需要对可能出现的错误进行处理,例如用户拒绝权限请求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/968910.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

lvs的DR模式

基于Linux的负载均衡集群软件 LVS 全称为Linux Virtual Server,是一款开源的四层(传输层)负载均衡软件 Nginx 支持四层和七层(应用层)负载均衡 HAProxy 和Nginx一样,也可同时支持四层和七层(应用层)负载均衡 基于Linux的高可用集群软件 Keepalived Keepalived是Linux…

STM32 RTC 实时时钟说明

目录 背景 RTC(实时时钟)和后备寄存器 32.768HZ 如何产生1S定时 RTC配置程序 第一次上电RTC配置 第1步、启用备用寄存器外设时钟和PWR外设时钟 第2步、使能RTC和备份寄存器访问 第3步、备份寄存器初始化 第4步、开启LSE 第5步、等待LSE启动后稳定状态 第6步、配置LSE为…

2024年12月电子学会青少年机器人技术等级考试(三级)理论综合真题

202412 青少年等级考试机器人理论真题三级 一、单选题 第 1 题 Arduino UNO/Nano主控板&#xff0c;程序模块如下&#xff0c;该模块运行后&#xff0c;引脚5输出的等效电压为0V&#xff0c;变量i对应的值是&#xff1f;&#xff08; &#xff09; A&#xff1a;0 B&#xff1…

分治中的快速排序(前序遍历)与归并排序(后序遍历)详细对比分析

目录 1. 快速排序&#xff08;前序遍历&#xff09; 核心思想与步骤 关键特性 示例分析 2. 归并排序&#xff08;后序遍历&#xff09; 核心思想与步骤 关键特性 示例分析 3. 对比总结 4. 选择依据与优化策略 5. 实际应用场景 6. 核心差异图示 7. 总结 1. 快速排序…

DeepSeek 助力 Vue 开发:打造丝滑的进度条

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

编译和链接【四】链接详解

文章目录 编译和链接【四】链接详解前言系列文章入口符号表和重定位表链接过程分段组装符号决议重定位 编译和链接【四】链接详解 前言 在我大一的时候&#xff0c; 我使用VC6.0对C语言程序进行编译链接和运行 &#xff0c; 然后我接触了VS&#xff0c; Qt creator等众多IDE&…

LeetCode每日精进:876.链表的中间结点

题目链接&#xff1a;876.链表的中间结点 题目描述&#xff1a; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5…

数据结构——结构体位域、typedef类型重定义、宏、共用体union、枚举、虚拟内存划分

一、结构体位域 1.1 结构体位域的基础 结构体位域&#xff1a;把结构体字节大小扣到极致的一个类型&#xff0c;以bit单位 格式&#xff1a;struct 位域体名{数据类型 位域名:位域大小;数据类型 位域名:位域大小;...};解析&#xff1a;位域体名&#xff1a;可有可无&#xff…

CZML 格式详解,javascript加载导出CZML文件示例

示例地址&#xff1a;https://dajianshi.blog.csdn.net/article/details/145573994 CZML 格式详解 1. 什么是 CZML&#xff1f; CZML&#xff08;Cesium Zipped Markup Language&#xff09;是一种基于 JSON 的文件格式&#xff0c;用于描述地理空间数据和时间动态场景。它专…

SQL递归技巧

1.读样例 with recursive cet_dpt(id, parent_id, path, org_category, level,depart_name) as (select id ,parent_id,depart_name as path,org_category,1 as level,sd.depart_namefrom isolarerp.sys_depart sdwhere del_flag 0and sd.org_code A09B15union al…

django配置跨域

1、第一种 from django.views.decorators.csrf import csrf_exemptcsrf_exempt第二种 安装 pip install django-cors-headers在配置文件settings.py进入 INSTALLED_APPS [..."corsheaders", # 添加 ]MIDDLEWARE [corsheaders.middleware.CorsMiddleware, # 添加…

设置mysql的主从复制模式

mysql设置主从复制模式似乎很容易&#xff0c;关键在于1&#xff09;主库启用二进制日志&#xff0c;2&#xff09;从库将主库设为主库。另外&#xff0c;主从复制&#xff0c;复制些什么&#xff1f;从我现在获得的还很少的经验来看&#xff0c;复制的内容有表&#xff0c;用户…

蓝耘智算平台:开启企业级 DeepSeek 智能助手的搭建捷径

文章目录 一、深度解密 DeepSeek 技术矩阵1.1 模型架构创新1.2 核心能力全景 二、私有化部署&#xff1a;企业的明智之选2.1 企业级部署场景2.2 硬件选型策略 三、蓝耘平台&#xff1a;部署全流程大揭3.1 环境准备阶段Step 1&#xff1a;访问蓝耘智算云官网完成企业认证Step 2&…

Android原生的HighCPU使用率查杀机制

摘要 原生的HighCPU使用率查杀机制是基于读取/proc/pid/stat中的utime stime后&#xff0c;根据CPU使用率 (utime stime / totalTime)*100%进行实现&#xff0c;当检测后台进程的CPU使用率超过阈值时&#xff0c;执行查杀和统计到电池数据中。 细节点&#xff1a; 1. 原生根…

数据库安全、分布式数据库、反规范化等新技术(高软19)

系列文章目录 3.7数据库安全、分布式数据库、反规范化等新技术 前言 本节数据库安全、分布式数据库、反规范化等新技术相关概念与技术。 一、数据库 1.数据库安全 2.数据库备份 二、分布式数据库 1.数据库分布 2.数据仓库 3.数据仓库结构 4.商业智能&#xff08;BI&#xf…

数据结构实现顺序表的尾插,尾删,按值查找/修改/删除,按下标查找/增加/删除

头文件&#xff1a;head.h #ifndef __HEAD_H__ #define __HEAD_H__#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAXSIZE 20enum num {success,false-1};typedef int datatype;typedef struct {int len;datatype data[MAXSIZE]; }S…

新手自学:如何用gromacs对简单分子复合物进行伞形采样

1、建立体系: 1、将蛋白的pdb文件转化为gmx: gmx pdb2gmx -f 2BEG_model1_capped.pdb -ignh -ter -o complex.gro 这个网页可以实现将多肽序列转化为pdb: ProBuilder On-line 这个教程的蛋白2BFG包含两条链(chain A和B) 在生成的topol文件中,增加如下的内容,效果就…

2025 BabitMF 第一期开源有奖活动正式开启 !

为了促进开源社区的交流与成长&#xff0c;字节跳动开源的多媒体处理框架 BabitMF &#xff08;GitHub - BabitMF/bmf: Cross-platform, customizable multimedia/video processing framework. With strong GPU acceleration, heterogeneous design, multi-language support, e…

Ollama 自定义导入模型

文章目录 一、从 GGUF 导入1.1 CCUF 介绍1.2 导入方式 二、由模型直接导入2.1 模型下载2.2 使用 llama.cpp 进行转换&#xff08;1&#xff09;克隆 llama.cpp 库到本地&#xff0c;并安装相关库&#xff08;2&#xff09;环境验证&#xff08;3&#xff09;执行转换程序 2.3 使…

J6 X8B/X3C切换HDR各帧图像

1、OV手册上的切换命令 寄存器为Ox5074 各帧切换&#xff1a; 2、地平线control tool实现切换命令 默认HDR模式出图&#xff1a; HCG出图&#xff1a; LCG出图 SPD出图 VS出图