最近项目上比较忙额没有时间写些东西分享出来,今天忙里偷闲决定把我封装的拍照组件分享出来。如果有兴趣的话看我之前写的文章应该能看到我有说过如何通过API来获取媒体设备,这次就将完整代码跟大家分享一下,代码我会贴在最后面。

一、实现原理

这个组件是web端的组件,使用的也是webAPI,我们最主要的API是navigator,这个API给了我们调用媒体设备的能力。在我们通过API获取到媒体数据后我们可以通过转换的形式将得到的流数据用video展示出来,具体操作可以看我之前的文章。接下来我们会用到画布,画布的强大我自不必多说,主要还是通过画布的drawImage方法来获取video标签的某一帧图片。

二、代码

1.html部分:

<template><el-dialog :title="title" :visible.sync="photoVisible" width="60%" :before-close="handleClose" custom-class="dialog-double"><div id="photo-wrapper"><div class="btn-group"><el-button class="changeButton" @click="getCompetence" type="primary">开启摄像头</el-button><el-button class="changeButton" @click="closeCamera" :disabled="closeAble && mediaStreamTrack">关闭摄像头</el-button><el-button class="changeButton" id="btn" type="primary">拍照</el-button><el-button class="changeButton" id="btn1" type="primary" @click="submit">提交图片</el-button></div><el-select v-if="devicesList.length > 1" v-model="currentDeviceID" clearable placeholder="请选择设备"><el-option v-for="device in devicesList" :key="device.deviceId" :label="device.label" :value="device.deviceId"></el-option></el-select><div class="show-box"><video src="" id="v1" :style="showStyle"></video><canvas id="c1" width="800" height="450" :style="{ display: 'none' }"></canvas><img id="img" src="" :style="showStyle" /></div></div></el-dialog>
</template>

2.script部分

export default {name: "BrowserPhoto",props: {photoVisible: {type: Boolean,default: false,},title: {type: String,default: "",},showStyle: {type: Object,default: () => ({width: "400px",}),},videoConfig: {type: Object,default: () => ({width: 1280, //1280height: 720, //720}),},// 是否在没有摄像头开启时禁用关闭按钮closeAble: {type: Boolean,default: false,},url: {type: String,},},data() {return {currentDeviceID: "", //当前媒体设备IDmediaStreamTrack: "", //媒体设备对象devicesList: [], //设备列表imgFile: "", //图片文件};},watch: {currentDeviceID(newV, oldV) {console.log(newV);if (newV !== oldV) {this.closeCamera();}},},methods: {searchDevice() {if (window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia) {}navigator.mediaDevices.enumerateDevices().then((devices) => {let videoList = devices.filter((item) => {return item.kind == "videoinput";});this.devicesList = videoList;if (videoList.length == 0) {this.$message({message: "没有查询到视频输入设备!",type: "error",});return false;} else if (videoList.length == 1) {this.currentDeviceID = videoList[0].deviceId;} else if (videoList.length > 1) {this.$message({message: "当前设备有多个视频输入设备,可根据需要切换视频输入设备",type: "info",});}});},getCompetence() {let video = document.querySelector("#v1");let photo = document.querySelector("#c1");let ctx = photo.getContext("2d");let btn = document.querySelector("#btn");let btn1 = document.querySelector("#btn1");let img = document.querySelector("#img");let download = document.querySelector("#download");const h = this.$createElement;navigator.mediaDevices.getUserMedia({video: {deviceId: this.currentDeviceID || this.devicesList[0],...this.videoConfig,},audio: false,}).then((stream) => {let mediaStreamTrack = stream.getTracks()[0];this.mediaStreamTrack = mediaStreamTrack;video.srcObject = stream;video.play();btn.addEventListener("click",() => {let devicePixelRatio = window.devicePixelRatio || 1;let backingStoreRatio =ctx.webkitBackingStorePixelRatio ||ctx.mozBackingStorePixelRatio ||ctx.msBackingStorePixelRatio ||ctx.oBackingStorePixelRatio ||ctx.backingStorePixelRatio ||1;let ratio = devicePixelRatio / backingStoreRatio;console.log(ratio);photo.width = photo.width * ratio;photo.height = photo.height * ratio;ctx.drawImage(video,0,0,800 * ratio,((800 * this.videoConfig.height) / this.videoConfig.width) * ratio);ctx.scale(ratio, ratio);// canvas.toDataURL 返回的是一串Base64编码的URLimg.src = photo.toDataURL("image/png");let arr = photo.toDataURL("image/png").split(",");let type = arr[0].match(/:(.*?);/)[1];let bstr = atob(arr[1]);let n = bstr.length;let u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}this.imgFile = new File([u8arr], "img" + new Date().getTime(), { type });console.log(this.imgFile);this.$emit("upload", this.imgFile);// 恢复宽高photo.width = photo.width / ratio;photo.height = photo.height / ratio;},false);}).catch((err) => {console.log(err);});},closeCamera() {console.log(this.mediaStreamTrack);if (this.mediaStreamTrack) {this.mediaStreamTrack.stop();}// else {//   this.$message({//     message: "关闭失败,请检查当前设备是否开启摄像头",//     type: "error",//   });// }},handleClose() {this.$emit("update:photoVisible", false);},submit() {let url = this.url;let param = new FormData();param.append("file", this.imgFile);let config = {headers: { "Content-Type": "multipart/form-data" },};http.post(url, param, config).then((res) => {console.log(res);this.$emit("uploadSuccess", res);});},},mounted() {this.searchDevice();// if(this.closeAble){// }},beforeDestroy() {this.closeCamera();},
};

三、注意事项

其实没啥好讲的,东西都在代码里,看就完事了,重点在于canvas生成的图片的模糊度问题,这一点除了和ratio有关外还和生成的图片页面比例有关,慢慢调就完事了。希望对大家有所帮助,开源互助。

浏览器拍照功能(Vue+ElementUI组件)相关推荐

  1. vue+elementUI组件table实现前端分页功能

    前端分页和后端分页就是请求的差异,前端分页的话只请求一次,所以要在render table组件的时候控制下数据 :data="tableData.slice((currentPage-1)* ...

  2. 基于vue element-ui 封装上传图片组件 功能:上传,删除,预览,上传图片水印,拖拽排序,上传进度条等

    学习目标: 我们在开发后台时肯定避免不了上传图片的功能 例如: 上传图片回显 上传完成 : 预览查看 , 删除等 如果是图片列表,还可能让你拖动图片排序 有的后台项目可能要给图片添加水印,添加标记 有 ...

  3. Vue基于ElementUI组件实现滑块登录验证组件

    引言 在实际项目应用开发中,为了防止用户频繁发起登录请求,导致后端登录访问压力瞬时过大,我们可以设计一些验证规则防刷,最常见的方式是通过输入验证码的方式降低刷新频率,后台通过返回不同的验证码从而降低用 ...

  4. VUE实现调用摄像头和拍照功能

    import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.cs ...

  5. elment上传pdf文件至服务器,基于element-ui组件手动实现单选和上传功能.pdf

    基基于于element-ui组组件件手手动动实实现现单单选选和和上上传传功功能能 在用户使用过程中提出一键导入的功能,需求如下:点击导入按钮显示提示框,然后是单选框以及上传按钮.这篇 文章 要介绍了基 ...

  6. vue 树形控件可编辑_vue.js element-ui组件改iview 第一期 tree树形控件

    此为第一期修改,后期也适配了其他组件,更多查看我得文章 element-ui组件的tree树形控件修改源码改为iview组件 实现原理 修改了element-ui源码,把源码里面的tree模块提取出来 ...

  7. vue实现网络监控摄像头直播拍照功能

    获取摄像机品牌的RTSP地址 比如海康的是: rtsp://user:pwd@ip:port/MPEG-4/ch1/main/av_stream 遗憾的是前端不支持rtsp,但支持rtmp,我们就要想 ...

  8. 【Vue3.0实战逐步深入系列】扩展投票功能基于elementui进行组件封装实现一个简单的问卷调查功能

    [千字长文,熬夜更新,原创不易,多多支持,感谢大家] 前言 小伙伴们大家好.在前面一偏文章中我们把投票功能进行了简单的改造:引入了axios第三方库并进行了二次封装用于模拟请求服务器数据.同时添加了一 ...

  9. 初识二维码 第二十讲 二维码解码程序的组件之一 摄像头拍照功能

    初识二维码 第二十讲    二维码解码程序的组件之一 摄像头拍照功能 解码程序的第一个环节是通过摄像头这个硬件,得到二维码的图片. 对摄像头的工作原理来说,简单的描述如下:1是从摄像头得到模拟信号, ...

最新文章

  1. LeetCode Additive Number(递归)
  2. SpringCloud微服务云架构构建B2B2C电子商务平台之-(八)消息总线(Spring Cloud Bus)...
  3. poj 1852 Ants_贪心
  4. mysql视图有哪几种_数据库报表的视图有几种?每种视图的功能是什么?
  5. JEPaas【按钮隐藏】根据单据状态值决定【添加明细】【删除】按钮的隐藏和显示
  6. HTML5网页APP连接MQTT,使用js连接mqtt
  7. 7. SVM松弛变量
  8. 微软正在开发基于 Rust 的安全编程语言
  9. 风控建模:催收评分卡(四)--变量整理除了跟数据获取相关外还跟什么有关系?
  10. exception e 是泛类吗_Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记
  11. 1032. Sharing (25)
  12. 蓝牙耳机测试软件apk_蓝牙耳机弹窗app下载-蓝牙耳机弹窗软件v3.1.0安卓版_5577安卓网...
  13. 联想thinkpad E430C硬盘位换为固态,硬盘放于光驱位(win7+win10+ubuntu三系统安装教程)
  14. 向下兼容性格什么意思_成大事者都有一种高段位能力:向下兼容
  15. Guiding Teacher Forcing with Seer Forcing for Neural Machine Translation翻译
  16. WordPress文章标题显示汉字方法
  17. 红楼梦人物分析系统c语言,红楼梦人物分析.doc
  18. Camera成像原理(raw图如何产生的)
  19. KSImageNamed 安装后无效解决方法 亲测有用
  20. 全网最全面工作流引擎Flowable完整教程之多实例会签

热门文章

  1. C++ 串口通信程序
  2. 前端优化首屏加载速度
  3. [4G5G专题-114]:部署 - LTE PRACH前导码格式、ZC序列的生成规则与规划
  4. c语言实现10个数由小到大排序(插入排序法)
  5. 等保2.0linux测评指南
  6. three.js中坐标系转换以及camera的position、lookAt与up属性理解
  7. pygame-KidsCanCode系列jumpy-part8-记录历史最高分
  8. Python 斐波那契数
  9. 海通股票交易系统通道接口错误怎么办
  10. 微信聊天记录丢失后的记录