最新要闻
- 今日热议:2022年我国人均存款近1.3万元 网友:又拖后腿了
- 【报资讯】一家四口在三亚溺水全部遇难:官方科普“离暗流”危险性
- 天天微动态丨2022收官!合资车时代被终结 “迪王”养成 大票车企失去肥年
- 【环球速看料】《小美人鱼》真人电影周边童书曝光 黑美人鱼好可爱
- 天天即时:双形态不入耳!讯飞开放式办公耳机iFLYBUDS Air图赏
- 小米MIX Fold 2厚度与戴壳iPhone 14 Pro Max相当 雷军:惊艳
- 当前通讯!3秒复制任何人的嗓音!微软音频版DALL·E细思极恐 连环境背景音也能模仿
- 每日热议!官宣:Intel发烧U回来了!350W 56核能打过AMD 280W 64核吗?
- 每日播报!人气爆棚!上美回应《中国奇谭》周边断货:已开足马力生产
- 当前头条:你买过哪些?苹果已售出23.2亿部手机 国人最爱iPhone 6
- 每日速递:美国加州风暴天气已致17死 有大树直接被连根拔起
- 环球速看:取消灵动岛!苹果iPhone 16 Pro将配备屏下Face ID
- 春运咋办?博主跑1千公里高速实测充电桩:有服务区一半都是坏的
- 【时快讯】《中国奇谭》口碑封神!仅上线三集 播放量突破5000万
- 【世界聚看点】CPU、显卡持续涨价!全球PC出货量暴跌 联想继续第一
- 全球今日报丨DJI这三个字母 是怎么占领你的背包的
广告
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
手机端H5 实现自定义拍照界面
手机端 H5 实现自定义拍照界面也可以使用 MediaDevices API 和
(资料图片)
首先,使用 MediaDevices.getUserMedia() 方法获取摄像头媒体流,并将其传递给
接着,使用 HTML 的
最后,使用 canvas.toDataURL() 方法将图像转换为 base64 格式,可以通过将其保存到本地或发送到服务器来存储照片。
但是需要注意的是,在手机端,调用摄像头需要在 HTTPS 或 localhost 下访问,还需要用户事先进行授权。
且在手机端可能会有些浏览器对于getUserMedia有所限制,需要额外兼容性处理。且手机端的实现需要考虑屏幕的方向,在绘制截图时需要根据不同的屏幕方向调整画布尺寸。
在手机端,为了让用户能够在页面中手动切换摄像头,需要检测手机端设备是否有多个摄像头,在有多个摄像头时,提供给用户切换摄像头的选项。
总之,通过使用 MediaDevices API 和
一、实现示例框架代码
使用 MediaDevices.getUserMedia() 方法获取摄像头媒体流,并将其传递给
// 调用摄像头function invokingCamera() { // 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。 // 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { // 首先,如果有getUserMedia的话,就获得它 const getUserMedia = navigator.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); }); } } // 手机可视区域宽度(请通过相关API获取真实宽度) const windowWidth = 375; // 手机可视区域高度(请通过相关API获取真实高度) const windowHeight = 700; const constraints = { audio: false, video: { // 前置摄像头 facingMode: "user", // 该属性相当于手机端的高 width: Math.max(windowWidth, windowHeight) - 120, // 减去 120 用于在页面底部放置拍照等功能按钮 // 该属性相当于手机端的宽 height: Math.min(windowWidth, windowHeight), } }; navigator.mediaDevices.getUserMedia(constraints) .then(function (stream) { const video = document.querySelector("camera"); // 旧的浏览器可能没有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); });}
使用 HTML 的
function takeSnapshot() { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const video = document.querySelector("video"); canvas.width = Math.min(video.videoWidth, video.videoHeight); canvas.height = Math.max(video.videoWidth, video.videoHeight); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // ****** 镜像处理 ****** function getPixel(imageData, row, column) { const uint8ClampedArray = imageData.data; const width = imageData.width; const height = imageData.height; const pixel = []; for (let i = 0; i < 4; i++) { pixel.push(uint8ClampedArray[row * width * 4 + column * 4 + i]); } return pixel; } function setPixel(imageData, row, column, pixel) { const uint8ClampedArray = imageData.data; const width = imageData.width; const height = imageData.height; for (let i = 0; i < 4; i++) { uint8ClampedArray[row * width * 4 + column * 4 + i] = pixel[i]; } } const mirrorImageData = ctx.createImageData(canvas.width, canvas.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); for (let h = 0; h < canvas.height; h++) { for (let w = 0; w < canvas.width; w++) { const pixel = getPixel(imageData, h, canvas.width - w - 1); setPixel(mirrorImageData, h, w, pixel); } } ctx.putImageData(mirrorImageData, 0, 0); // ****** 镜像处理 ****** const base64 = canvas.toDataURL("image/jpeg");}
最后,使用 canvas.toDataURL() 方法将图像转换为 base64 格式,可以通过将其保存到本地或发送到服务器来存储照片
二、具体实现代码(基于uni-app)
布局代码:
JavaScript 代码:
export default { data() { return { imageUrl: "", // 媒体流,用于关闭摄像头 mediaStreamTrack: null, }; }, onLoad() { this.invokingCamera(); }, onUnload() { this.handlePhotographCloseClick(); }, methods: { invokingCamera() { const self = this; // 注意本例需要在HTTPS协议网站中运行,新版本Chrome中getUserMedia接口在http下不再支持。 // 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象 if (navigator.mediaDevices === undefined) { navigator.mediaDevices = {}; } // 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia // 因为这样可能会覆盖已有的属性。这里我们只会在没有getUserMedia属性的时候添加它。 if (navigator.mediaDevices.getUserMedia === undefined) { navigator.mediaDevices.getUserMedia = function (constraints) { // 首先,如果有getUserMedia的话,就获得它 const getUserMedia = navigator.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); }); } } uni.getSystemInfo({ success: function (res) { const constraints = { audio: false, video: { // 前置摄像头 facingMode: "user", // 手机端相当于高 width: Math.max(res.windowWidth, res.windowHeight) - 120, // 手机端相当于宽 height: Math.min(res.windowWidth, res.windowHeight), } }; navigator.mediaDevices.getUserMedia(constraints) .then(function (stream) { self.mediaStreamTrack = stream; const 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); }); } }); }, handlePhotographCloseClick() { if (this.mediaStreamTrack) { // 关闭摄像头 this.mediaStreamTrack.getTracks().forEach(function (track) { track.stop(); }); this.mediaStreamTrack = null; } }, handlePhotographClick() { const self = this; const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const video = document.querySelector("video"); canvas.width = Math.min(video.videoWidth, video.videoHeight); canvas.height = Math.max(video.videoWidth, video.videoHeight); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // ****** 镜像处理 ****** function getPixel(imageData, row, column) { const uint8ClampedArray = imageData.data; const width = imageData.width; const height = imageData.height; const pixel = []; for (let i = 0; i < 4; i++) { pixel.push(uint8ClampedArray[row * width * 4 + column * 4 + i]); } return pixel; } function setPixel(imageData, row, column, pixel) { const uint8ClampedArray = imageData.data; const width = imageData.width; const height = imageData.height; for (let i = 0; i < 4; i++) { uint8ClampedArray[row * width * 4 + column * 4 + i] = pixel[i]; } } const mirrorImageData = ctx.createImageData(canvas.width, canvas.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); for (let h = 0; h < canvas.height; h++) { for (let w = 0; w < canvas.width; w++) { const pixel = getPixel(imageData, h, canvas.width - w - 1); setPixel(mirrorImageData, h, w, pixel); } } ctx.putImageData(mirrorImageData, 0, 0); // ****** 镜像处理 ****** self.$nextTick(() => { const base64 = canvas.toDataURL("image/jpeg"); self.imageUrl = base64; self.handlePhotographCloseClick(); }); }, handleAddPhotographClick() { this.uploadImage(); }, uploadImage: function () { const self = this; uni.chooseImage({ count: 1, sizeType: ["compressed"], success: function (res) { self.handlePhotographCloseClick(); const file = res.tempFiles[0]; const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function (e) { self.imageUrl = e.target.result; } } }); }, }};
样式代码:
最终效果展示:
手机端H5 实现自定义拍照界面
今日热议:2022年我国人均存款近1.3万元 网友:又拖后腿了
【报资讯】一家四口在三亚溺水全部遇难:官方科普“离暗流”危险性
天天微动态丨2022收官!合资车时代被终结 “迪王”养成 大票车企失去肥年
【环球速看料】《小美人鱼》真人电影周边童书曝光 黑美人鱼好可爱
天天即时:双形态不入耳!讯飞开放式办公耳机iFLYBUDS Air图赏
小米MIX Fold 2厚度与戴壳iPhone 14 Pro Max相当 雷军:惊艳
当前通讯!3秒复制任何人的嗓音!微软音频版DALL·E细思极恐 连环境背景音也能模仿
每日热议!官宣:Intel发烧U回来了!350W 56核能打过AMD 280W 64核吗?
每日播报!人气爆棚!上美回应《中国奇谭》周边断货:已开足马力生产
Shell 命令奇淫技巧,就是有点短
当前头条:你买过哪些?苹果已售出23.2亿部手机 国人最爱iPhone 6
每日速递:美国加州风暴天气已致17死 有大树直接被连根拔起
环球速看:取消灵动岛!苹果iPhone 16 Pro将配备屏下Face ID
春运咋办?博主跑1千公里高速实测充电桩:有服务区一半都是坏的
【时快讯】《中国奇谭》口碑封神!仅上线三集 播放量突破5000万
【世界聚看点】CPU、显卡持续涨价!全球PC出货量暴跌 联想继续第一
简讯:(五)elasticsearch 源码之查询流程分析
环球热文:消息服务 + Serverless 函数计算如何助力企业降本提效?
世界滚动:el-table更新数据页面闪烁问题
全球今日报丨DJI这三个字母 是怎么占领你的背包的
环球热头条丨告别毛巾“一条恒久远”!金号纯棉抑菌毛巾大促:一条5块钱
今日快讯:哪吒汽车联手宁德时代共研“滑板底盘”:电池、底盘合体
世界信息:苹果加大降低中国工厂依赖程度:都要搬走?印度成香饽饽 出口激增
独占4K AMR 120帧高规格!《流浪地球2》发布CINITY海报
世界热点!指针知识点总结
每日热讯!TiDB 底层存储结构 LSM 树原理介绍
linux基础:2、前期必备知识、系统运行命令、快捷方式命令、目录结构相关命令、文件与文件夹相关命令、目录结构
环球今日报丨C# 循环给多个连续编号的控件赋值
网上银行怎么转账?网上银行转账限额是多少?
诺基亚5800xm当年多少钱?诺基亚5800XM手机参数
投影仪吊架怎么安装?吊式投影仪安装方法
华为gt2怎么设置相册表盘?华为gt2有血氧功能吗?
唐门鸟翔碧空在哪里学?唐门鸟翔碧空可以放什么技能?
雷龙鱼水温多少合适?雷龙吃什么饲料?
最新消息:三星Galaxy S23系列定档:2月2日登场 首发新版骁龙8 Gen2
《满江红》公布秦桧版预告:饰演者雷佳音狠辣狡诈
【全球新要闻】特斯拉大降价 其它车企跟不跟?乘联会秘书长发声
焦点速读:万物有灵 被收养流浪狗跳车拦住怀孕主人 下一秒山路塌方
每日聚焦:1208元!中国探月航天推出限量火箭碎片:运送嫦娥四号的长三乙
加减乘除是谁发明的?加减乘除混合运算100道
米亲韩语是什么意思?韩语shake it是什么意思?
全高清和超高清有什么区别?全高清和超高清4K哪个更护眼?
异丙醇的作用与用途有哪些?异丙醇和酒精的区别是什么?
Serverless 奇点已来,下一个十年将驶向何方?
每日热点:没电、没网也能支付 数字人民币全新功能上线:安卓先行
环球微资讯!用上比亚迪发动机 斯威大虎ED-i增程版亮相:油耗低至2.06升
关注:公司就给员工加薪50元致歉 已尽力对不起大家引热议:为何不知足?
Win7彻底停服 国产OS统信站出来了:打印机、软件轻松迁移
读编程与类型系统笔记04_类型安全
微动态丨【QtJson】用Qt自带的QJson,直接一步到位封装和解析一个类的实例对象!
还买iPhone 14/15?新iPhone准备中:苹果弃灵动岛 更完美
人民日报评电视收费乱象:从用户身上“薅羊毛” 广告还见缝插针
微头条丨今年首场寒潮来袭 最强雨雪下在哪?这些地区将迎暴雨、暴雪
天天滚动:2022年 我把比亚迪DM-i插混当纯电动车 开了1万公里
《王者荣耀》星会员正式上线:全新充值体系 打破贵10等级限制
当前速讯:神舟新款游戏本上架:13代i5+满血RTX 3050 4999元
最强高端!小米13 Ultra有望下月发:终于告别USB 3.0
动态:家长炮轰《中国奇谭》烂 吓哭孩子引热议:专家回应 给成年人看
环球观焦点:贾跃亭又摊上事了:法拉第未来接到纳斯达克摘牌警告
全球报道:JavaScript 扁平与树形数组数据的转换
热点在线丨层叠样式表(CSS)1
当前最新:ceph-3
世界速读:区块链特辑——solidity语言基础(四)
热文:华硕全球首秀四频段Wi-Fi 7路由器:峰值下载2.5万兆
滚动:惠普新款EliteBook 1040笔记本发布:13代酷睿、2K 120Hz屏
天天视点!为什么一个病毒株传着传着就没了?
Redmi K40S 12+256G顶配版不到1900元:骁龙870和OIS都有
各大新能源车企年度KPI出炉 特斯拉未达标 比亚迪称王
焦点速读:区块链特辑——solidity语言基础(二)
每日时讯!酷安最火骁龙8系手机诞生!一加11酷安热度第一
时隔两年 国美真快乐App重新更名国美
快看点丨大前端html学习06-宽高自适应
天天观察:SpringDataJPA 程序未配置乐观锁的情况下,报了乐观锁异常
Redis的客户端
每日时讯!关于19c RU补丁报错问题的分析处理
环球简讯:记录使用adb连接rn项目进行开发
焦点要闻:加油被惊喜到!一加11不杀后台:前一天打开的APP第二天还在
最新快讯!Python中高阶函数与装饰器教程
【新要闻】Python中的异常处理总结
flutter 效果实现 —— 全面屏效果
Mysql中的锁:表、MDL、意向锁、行锁
温子仁恐怖片新作《梅根》 拯救了北美院线一月票房
天天热点评!被假货逼疯的劳力士:终于坐不住了
全球快资讯:要的就是销量!特斯拉在新加坡优惠近7万
全球新资讯:豆瓣9.5高分动画!有家长炮轰《中国奇谭》画风吓哭孩子 网友不乐意了
全球今日报丨【首页】热销爆品开发修改商品值
加了国六B汽油 排气管喷水?网友犀利吐槽:我加了拉肚子
热讯:峰米S5 Rolling投影仪发布:360度可旋转支架 还能当音箱用
世界热推荐:米哈游创始人之一参与打造:国产独立游戏《微光之镜》今日发售
即时看!奥迪RS e-tron GT很好 但它仍是大众体系里最拧巴的产品
当前讯息:菜鸟将投2亿:补贴快递员爬楼送货上门
天天快看:iOS 16卡壳了
世界热推荐:OLED+彩色水墨翻转双屏!联想公布ThinkBook Plus Twist笔记本
天天微速讯:雷军爆料了!新机皇小米13 Ultra或将于MWC发布:影像堆料突破天际
焦点速看:因易增加儿童哮喘几率:美国或将禁止使用燃气灶
【环球播资讯】曾狠坑过乐视!中电熊猫被拉横幅维权:“还我血汗钱”
快消息!事件处理_2事件修饰符
flutter 基础 —— 事件监听
环球热门:JavaScript 将base64 转换为File