h5实现相机

news2024/11/20 21:36:28

什么是取景器

取景器是什么?取景器是相机的一个专业术语,在前端就是扫描拍照

取景器的实现原理

请求手机的一个媒体类型的视频轨道,利用一个div或者图片作为上层蒙层,然后在利用canvas绘制视频中某一帧的画面绘制为图片。

前期知识准备

- # MediaDevices.getUserMedia()

MediaDevices.getUserMedia() - Web API 接口参考 | MDN 在mdn中介绍了,这个api会调取摄像头,获取一个媒体轨道,这里我们只需要重点关注视频轨道,mdn对api的讲解也很清楚,下面是mdn的做好兼容的代码只需要直接使用

值得注意的点

  1. 该api必须https
  2. 兼容老版本浏览器
// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {
  navigator.mediaDevices = {};
}

// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有 getUserMedia 属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {
  navigator.mediaDevices.getUserMedia = function(constraints) {

    // 首先,如果有 getUserMedia 的话,就获得它
    var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

    // 一些浏览器根本没实现它 - 那么就返回一个 error 到 promise 的 reject 来保持一个统一的接口
    if (!getUserMedia) {
      return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
    }

    // 否则,为老的 navigator.getUserMedia 方法包裹一个 Promise
    return new Promise(function(resolve, reject) {
      getUserMedia.call(navigator, constraints, resolve, reject);
    });
  }
}

navigator.mediaDevices.getUserMedia(
{
                audio: false, //不需要获取声音
                video: {
                    //后置摄像头
                    facingMode: 'environment',
                    //分辨率,理想是2040,如果没有,最小1280,没有设置会比较模糊
                    width: { min: 1280, ideal: 2040 },
                    height: { min: 720, ideal: 1080 },
                },
            }
)
.then(function(stream) {
  var video = document.querySelector('video');
  // 旧的浏览器可能没有 srcObject
  if ("srcObject" in video) {
    video.srcObject = stream;
  } else {
    // 防止在新的浏览器里使用它,应为它已经不再支持了
    video.src = window.URL.createObjectURL(stream);
  }
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});
  • canvas绘图-drawImage

HTML5 canvas drawImage() 方法 canvas绘制,主要是要注意绘制过程中的参数,w3cschool也写的非常清楚,暂不做重复说明

案例源码

html部分

<div class="viewfinder">
        <!-- 用于兼容,若不能使用,调用手机拍照功能 -->
        <input id="file" type="file" accept="image/*" capture="camera" style="display:none"/>

        <div class="wrap-box">
            <!-- 视频整个页面 -->
            <video style="height: 100vh;width: 100vw;position: fixed;top: 0;left: 0;object-fit: cover"/>
            <div class="imgshow">
              
                <div v-if="status==1"  class="box"></div>
                <!-- b.拍摄完后展示抓拍图片 -->
                <img v-if="status==2" :src="imageUrl" alt="" class="wrapImg">
            </div>

            <button class="snap" @click="snapPhoto">拍照</button>

            <!-- 抓拍 -->
            <canvas id="mycanvas" style="visibility: hidden;"/>
        </div>
    </div>

css部分

<style lang="less" scoped>
.wrap-box {
    width: 100%; 
    position: fixed; 
    left: 0; 
    height: 100%;
    .imgshow {
        width: 100%; 
        position: absolute; 
        left: 0; 
        bottom: 25vh; 
        top: 25vh; 
        right: 0;
    }
    .box {
        width: 100%;
        height: 100%;
        border: 10px solid rgba(0,0,0,0.5);box-sizing: border-box;
    }
    .wrapImg{
        width:100%;
        height: 100%;
    }
    .snap {
        position: fixed; 
        left: 0;
        top: 2vh;
        z-index: 1000;
    }
}
</style>

js逻辑部分

<script>
export default {
    data() {
        return {
            status: 0, // 自定义相机-拍摄进度:0|未开启 1|开启但未拍摄 2|开启且已拍摄
            imageUrl: '',
        };
    },
    mounted() {
        //打开视频轨道
        this.openCamera();
    },
    methods: {
        openCamera() {
            const constraints = {
                audio: false,
                video: {
                    facingMode: 'environment',
                    width: { min: 1280, ideal: 2040 },
                    height: { min: 720, ideal: 1080 }
                },
            };

            // 兼容
            if (navigator.mediaDevices === undefined) navigator.mediaDevices = {};

            if (navigator.mediaDevices.getUserMedia === undefined) {
                navigator.mediaDevices.getUserMedia = function(constraints) {
                    const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
                    if (!getUserMedia) {
                        return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                    }
                    return new Promise(function(resolve, reject) {
                        getUserMedia.call(navigator, constraints, resolve, reject);
                    });
                };
            }

            // 获取视频流
            const that = this;
            navigator.mediaDevices.getUserMedia(constraints)
                .then(function(stream) {
                    const video = document.querySelector('video');
                    video.srcObject = stream;
                    video.onloadedmetadata = function() {
                        video.play();
                    };
                    that.status = 1;
                })
                .catch(function(err) {
                    // 不兼容调用原始相机拍照;
                    that.originCamera();
                });
        },
        originCamera() {
            const promise = new Promise(function(resolve, reject) {
                const file = document.getElementById('file');
                file.click();
                file.onchange = function(event) {
                    if (!event) {
                        reject('empty');
                    }
                    // 当选中或者拍摄后确定图片后
                    const file = event.target.files[0];
                    resolve(file);
                };
            });
            promise.then(value => {
                //提交照片
                that.submitPhoto('origin', value);
            }
            );
        },
        snapPhoto() {
            const canvas = document.querySelector('#mycanvas');
            const video = document.querySelector('video');
            let width = canvas.width = video.videoWidth;
            let height = canvas.height = video.videoHeight;
            let cut_y = height/4
            //只画取景框内的图片
            canvas.getContext('2d').drawImage(video, 0, cut_y, width, cut_y*2, 0, 0, width, height);
            // 将canvas保存为图片
            this.imageFile = this.canvasToFile(canvas);

            // blob转url:用于展示
            const p = new Promise((resolve) => {
                canvas.toBlob(blob => {
                    const url = URL.createObjectURL(blob);
                    resolve(url);
                });
            });
            const that = this;
            p.then(value => {
                that.imageUrl = value;
                that.status = 2;// 表示拍摄完成
            });
        },
        canvasToFile(canvas) {
            const dataurl = canvas.toDataURL('image/png');
            let arr = dataurl.split(','),
                mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]),
                n = bstr.length,
                u8arr = new Uint8Array(n);
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            const file = new File([ u8arr ], 'phone.png', { type: mime });
            return file;
        },
        // 提交
        submitPhoto(type, file) {
            if (type == 'origin') {
                this.imageFile = file;
            }
        },
    },
};
</script>

整个功能就完结啦 参考以下文章

  • Html5调用手机摄像头后添加取景框并使用WebUploader上传 - 灰信网(软件开发博客聚合)
  • 兼容性相关:h5调用摄像头拍照兼容性及原生实现拍照取景框 - 简书

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML基础知识

一个网站由两部分组成&#xff1a;前端和后端。前端主流语言目前是HTML、CSS、JS等。HTML只是描述了页面的内容&#xff08;骨架&#xff09;&#xff0c;CSS才是描述了页面的样式。HTML结构HTML标签HTML代码是由“标签”构成的&#xff0c;HTML描述了页面上有什么东西&#xf…

数字化转型导师坚鹏:银行数字化转型为什么需要融合王阳明心学

在BLM银行数字化转型方法论中&#xff0c;我之所以融合BLM模型与王阳明心学&#xff0c;作为一个工科背景并拥有多年软硬件产品研发经验的人来说&#xff0c;深刻地知道很多人利用了科技的力量做了大量的恶事&#xff0c;而不是善事&#xff0c;如黑客大量盗取、泄漏、贩卖客户…

ESLint 的一些理解

ESLint ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具&#xff0c;它的目标是保证代码的一致性和避免错误。 为什么要使用ESLint 有的可以帮我们避免错误&#xff1b;有的可以帮我们写出最佳实践的代码&#xff1b;有的可以帮我们规范变量的使用方式&a…

Docker入门之使用Dockerfile 构建镜像(七)

文章目录1. 前言2. Docker file 核心要点2.1 注意事项2.2 Docker file 执行流程2.3 Docker Image、Docker file、Docker Container区别2.4 Dockerfile常用保留字指令2.4.1 FROM2.4.2 MAINTAINER2.4.3 RUN2.4.4 EXPOSE2.4.5 WORKDIR2.4.6 USER2.4.7 ENV2.4.8 ADD2.4.9 COPY2.4.1…

ansible 简单使用

运行过程 1.加载自己的配置文件&#xff0c;默认/etc/ansible/ansible.cfg&#xff1b; 2.查找对应的主机配置文件&#xff0c;找到要执行的主机或者组&#xff1b; 3.加载自己对应的模块文件&#xff0c;如 command&#xff1b; 4.通过ansible将模块或命令生成对应的临时py文…

OpenMMLab 实战营打卡 - 第 一 课

OpenMMLab 实战营打卡 - 第 一 课 复习下总忘的基础知识 卷积的通道数变化 前一层特征纬度&#xff08;通道数&#xff09;决定核的通道数 当前层输出的特征纬度&#xff0c;由核的数量决定 图像尺寸变化 padding 公式&#xff1a;H′H−K12pH^{\prime}H-K12 pH′H−K12p…

电源技术中的安森美 单通道电压电平转换器件FXLP34P5X 适合便携式应用方案

电源技术中的安森美 单通道电压电平转换器件FXLP34P5X 适合便携式应用方案 &#xff1a;输入转换器电源电压为VCC1&#xff0c;输出转换器电源电压为VCC。 该器件使用1.0V至3.6V的VCC值运行&#xff0c;主要用于要求超低功耗的便携式应用。内部电路由最小量的缓冲器级组成&…

普通大学生自学 JAVA 怎样才能进大厂?

前言 可以看一下现在大厂对于Java方面的要求 阿里 百度 腾讯 从上面可以看出&#xff0c;无论是阿里、百度亦或是腾讯对于Java方面的要求是比较高的&#xff0c;可以说要求的是一个全面&#xff0c;所以想要进入大厂&#xff0c;不能操之过急&#xff0c;需要先从基础做起&am…

php报错SERVER SENT CHARSET (255) UNKNOWN

配置文件PHP.ini修改打开; extension_dir "ext"&#xff0c;修改成; extension_dir "./" ; On windows: extension_dir "自己php的存放路径\ext"2.打开extensionmsql.dll; For example, on Windows: ;extensionmsql.dll3.修改配置&#xff08…

五、Linux 用户管理常用命令

一、用户管理命令 - useradd 命令名称&#xff1a;useradd 命令所在路径&#xff1a;/usr/sbin/useradd 执行权限&#xff1a;root 功能描述&#xff1a;添加新用户 语法&#xff1a;useradd 用户名 二、用户管理命令 - userdel 命令名称&#xff1a;userdel 命令所在路…

创业青年张继群

中央广播电视总台 -专访-张继群简介&#xff1a; 张继群&#xff0c;1995年10月生&#xff0c;男&#xff0c;临沂大学硕士研究生在读&#xff0c;现临沂城投思索信息技术有限公司智慧城市事业部员工&#xff0c;作为农业专班成员主要从事网络安全、大数据等新一代信息技术的科…

nuxt3:postcss-pxtorem

一、理解postcsshttps://www.postcss.com.cn/1.1、PostCSS是一个用 JavaScript 工具和插件转换 CSS 代码的工具。1.2、增强代码可读性&#xff1a;利用从 Can I Use 网站获取的数据为 CSS 规则添加特定厂商的前缀。 Autoprefixer 自动获取浏览器的流行度和能够支持的属性&#…

如何录制电脑屏幕和声音?分享3个实用的方法,赶紧收藏

使用电脑录屏工具&#xff0c;可以帮助我们轻松录制电脑屏幕。有时候我们不仅仅需要录制电脑上的画面&#xff0c;还需要在录制画面的同时录入声音。那您知道如何录制电脑屏幕和声音吗&#xff1f;如何在录屏的时候录制电脑内部声音或者电脑外部声音&#xff1f;现在小编就给大…

操作系统—王道考研之计算机系统概述

by:星辰 课程视频链接:https://www.bilibili.com/video/BV1YE411D7nH 第 1 章 计算机系统概述 1.1 操作系统的基本概念 1.1.1 操作系统的概念、功能和目标 系统资源的管理者、提供接口、作为扩充机器、虚拟机 1.1.1.1 熟悉的操作系统举例 1.1.1.2 操作系统的层次结构 1.1.1…

5分钟了解 KubeGems 1.23 GA

KubeGems 是一款开源的企业级多租户容器云平台。围绕云原生社区&#xff0c;KubeGems 提供了多 Kubernetes 集群接入能力&#xff0c;并具备丰富的组件管理和资源成本分析功能&#xff0c;能够帮助企业快速的构建和打造一个本地化、功能强大且低成本的云管理平台。 KubeGems 发…

C 语言判断

判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 false。下面是大…

爱情呼叫转移-深度广度遍历(中)

广度遍历和深度遍历可以结合吗&#xff1f; 可以的&#xff0c;例如经典的《爱情转移》 《爱情转移》是电影《爱情呼叫转移》的主题曲&#xff0c;《爱情呼叫转移》讲述了一个男人穿梭在12个女人之间的情感流浪&#xff0c;讨论的是夫妻相处之道。我当时还在想他到底爱谁。 …

java 设计原则

提示&#xff1a; 设计模式 文章目录一、软件设计原则1.开闭原则2.依赖倒置原则3.单一职责原则4.接口隔离原则5.迪米特法则6.里氏替换原则7.合成复用原则一、软件设计原则 1.开闭原则 开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;是指一个软件实体如类、模块…

Java:如何避免Java内存泄漏

Java的核心优势之一就是利用JVM(Java虚拟机)&#xff0c;JVM是一种开箱即用的内存管理。你只管创建对象&#xff0c;Java的垃圾回收器帮你分配以及回收内存。然而&#xff0c;实际的情况并没有那么简单&#xff0c;因为内存泄漏在Java应用程序中还是时有发生的。为了避免内存泄…

别再焦虑了,进大厂没你想象的那么困难....

前段时间有个在小公司干了好多年的朋友离职了&#xff0c;想要拼一拼大厂&#xff0c;又觉得自己30多岁了&#xff0c;年级比较大&#xff0c;害怕人家不要。在我们的鼓励下&#xff0c;他选择字节跳动试试。面试总共花费了 20 天左右&#xff0c;包含了 4 轮电话面试、1 轮笔试…