前端黑科技:使用 JavaScript 实现网页扫码功能
在数字化时代,二维码已经渗透到我们生活的方方面面。从移动支付到产品溯源,二维码凭借其便捷性和高效性,成为了信息传递的重要载体。而随着前端技术的不断发展,我们甚至可以使用 JavaScript 在网页端实现二维码扫描功能,为用户提供更加便捷的操作体验。
本文将带您深入了解如何使用 JavaScript 调用摄像头,结合 jsQR 库,以及如何控制闪光灯,最终实现一个功能完善的网页扫码应用。
一、 项目概述
我们将创建一个简单的网页应用,该应用能够:
- 调用设备摄像头,获取实时视频流。
- 在网页上创建一个扫描区域,用户可以将二维码放置在该区域内进行扫描。
- 使用
jsQR库解码扫描区域内的二维码图像数据,获取二维码内容。 - 提供手动输入二维码内容的功能。
- 如果设备支持,还可以控制闪光灯的开关,以便在光线不足的情况下进行扫描。
二、 实现步骤
1. HTML 结构
首先,我们需要构建基本的 HTML 结构,包括:
<video>标签:用于展示摄像头捕获的实时视频流。<canvas>标签:用于绘制视频帧和扫描区域,并从中获取图像数据。<div>标签:用于创建扫描区域、按钮组等 UI 元素。
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>扫一扫</title><link rel="stylesheet" href="style.css">
</head>
<body><video id="video" autoplay></video><canvas id="overlay" hidden></canvas><div class="scan-area"></div><div class="btn-group"><button id="manualInputBtn">手动输入</button><button id="flashBtn">闪光灯</button></div><script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script><script src="script.js"></script>
</body>
</html>
2. CSS 样式
为了提升用户体验,我们需要为页面元素添加一些样式:
/* style.css */
body {margin: 0;overflow: hidden;
}#video {width: 100%;height: auto;display: block;
}#overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;
}.scan-area {border: 3px solid yellow;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 80%;height: 30%;
}/* ...其他样式 */
3. JavaScript 交互
JavaScript 代码是实现扫码功能的核心部分,主要包括以下几个步骤:
-
获取摄像头权限: 使用
navigator.mediaDevices.getUserMedia()方法请求访问用户的摄像头。 -
播放视频流: 将获取到的视频流赋值给
<video>标签的srcObject属性,并调用video.play()方法开始播放。 -
创建扫描循环: 使用
requestAnimationFrame()方法创建一个循环,不断地从视频流中获取帧图像,并进行二维码解码。 -
绘制视频帧: 在每一帧中,使用
canvas.drawImage()方法将视频帧绘制到<canvas>元素上。 -
获取扫描区域图像数据: 使用
canvas.getImageData()方法获取扫描区域的图像数据。 -
解码二维码: 使用
jsQR库的jsQR()方法解码图像数据,如果解码成功,则获取二维码内容。 -
处理扫描结果: 对解码后的二维码内容进行处理,例如跳转到链接、显示信息等。
-
实现其他功能: 实现手动输入二维码内容和控制闪光灯等功能。
// script.js
const video = document.getElementById('video');
const overlay = document.getElementById('overlay');
const manualInputBtn = document.getElementById('manualInputBtn');
const flashBtn = document.getElementById('flashBtn');
const scanArea = document.querySelector('.scan-area');let stream;
let scanning = false;
let flashEnabled = false;// 获取摄像头权限并开始播放视频流
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(s => {stream = s;video.srcObject = stream;video.play();// 开始扫描scanning = true;requestAnimationFrame(scan);}).catch(err => {console.error("无法访问摄像头:", err);});// 扫描二维码
function scan() {if (scanning) {const canvas = overlay.getContext('2d');const videoWidth = video.videoWidth;const videoHeight = video.videoHeight;// 设置画布大小overlay.width = videoWidth;overlay.height = videoHeight;// 将视频帧绘制到画布上canvas.drawImage(video, 0, 0, videoWidth, videoHeight);// ...获取扫描区域图像数据// 使用 jsQR 库解码二维码const code = jsQR(imageData.data, imageData.width, imageData.height);// 如果成功解码,则停止扫描并处理结果if (code) {scanning = false;handleScanResult(code.data);} else {requestAnimationFrame(scan);}}
}// 处理扫描结果
function handleScanResult(data) {alert("扫描结果:" + data);// 这里可以根据扫描结果进行相应的操作,例如跳转到链接或显示信息
}// 手动输入按钮点击事件
manualInputBtn.addEventListener('click', function() {// ...
});// 闪光灯按钮点击事件
flashBtn.addEventListener('click', function() {// ...
});
三、完整代码
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>扫一扫</title><style>body {margin: 0;overflow: hidden;}#video {width: 100%;height: auto;display: block;}#overlay {position: absolute;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;}.scan-area {border: 3px solid yellow;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);width: 80%;height: 30%;}.btn-group {position: absolute;bottom: 20px;left: 50%;transform: translateX(-50%);display: flex;}button {background-color: rgba(0, 0, 0, 0.5);color: white;border: none;padding: 10px 20px;margin: 0 10px;border-radius: 5px;font-size: 16px;cursor: pointer;}</style>
</head>
<body><video id="video" autoplay></video>
<canvas id="overlay" hidden></canvas><div class="scan-area"></div><div class="btn-group"><button id="manualInputBtn">手动输入</button><button id="flashBtn">闪光灯</button>
</div><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<script>const video = document.getElementById('video');const overlay = document.getElementById('overlay');const manualInputBtn = document.getElementById('manualInputBtn');const flashBtn = document.getElementById('flashBtn');const scanArea = document.querySelector('.scan-area');let stream;let scanning = false;let flashEnabled = false;// 获取摄像头权限并开始播放视频流navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(s) {stream = s;video.srcObject = stream;video.play();// 开始扫描requestAnimationFrame(scan);}).catch(function(err) {console.error("无法访问摄像头:", err);});// 扫描二维码function scan() {if (scanning) {const canvas = overlay.getContext('2d');const videoWidth = video.videoWidth;const videoHeight = video.videoHeight;// 设置画布大小overlay.width = videoWidth;overlay.height = videoHeight;// 将视频帧绘制到画布上canvas.drawImage(video, 0, 0, videoWidth, videoHeight);// 获取扫描区域的坐标和尺寸const scanAreaRect = scanArea.getBoundingClientRect();const scanAreaX = scanAreaRect.left;const scanAreaY = scanAreaRect.top;const scanAreaWidth = scanAreaRect.width;const scanAreaHeight = scanAreaRect.height;// 获取扫描区域的图像数据const imageData = canvas.getImageData(scanAreaX, scanAreaY, scanAreaWidth, scanAreaHeight);// 使用 jsQR 库解码二维码const code = jsQR(imageData.data, imageData.width, imageData.height);// 如果成功解码,则停止扫描并处理结果if (code) {scanning = false;handleScanResult(code.data);} else {requestAnimationFrame(scan);}}}// 处理扫描结果function handleScanResult(data) {alert("扫描结果:" + data);// 这里可以根据扫描结果进行相应的操作,例如跳转到链接或显示信息}// 手动输入按钮点击事件manualInputBtn.addEventListener('click', function() {const input = prompt("请输入二维码内容:");if (input) {handleScanResult(input);}});// 闪光灯按钮点击事件flashBtn.addEventListener('click', function() {if ('torch' in navigator.mediaDevices.getUserMedia({ video: true })) {flashEnabled = !flashEnabled;stream.getVideoTracks()[0].applyConstraints({advanced: [{ torch: flashEnabled }]});// 更新按钮文本flashBtn.textContent = flashEnabled ? '关闭闪光灯' : '闪光灯';} else {alert('您的设备不支持闪光灯功能。');}});// 开始扫描scanning = true;
</script></body>
</html>
四、 总结
通过以上步骤,我们成功地使用 JavaScript 在网页端实现了二维码扫描功能。该功能可以广泛应用于各种场景,例如:
- 移动支付: 用户可以使用手机扫描网页上的二维码完成支付。
- 产品溯源: 用户可以扫描产品上的二维码,查看产品信息、生产日期、物流信息等。
- 活动签到: 用户可以使用手机扫描二维码完成活动签到。
随着 Web 技术的不断发展,相信未来会有更多创新的应用场景出现。
希望本文能够帮助您了解网页扫码功能的实现原理,并激发您探索更多前端黑科技的兴趣。
相关文章:
前端黑科技:使用 JavaScript 实现网页扫码功能
在数字化时代,二维码已经渗透到我们生活的方方面面。从移动支付到产品溯源,二维码凭借其便捷性和高效性,成为了信息传递的重要载体。而随着前端技术的不断发展,我们甚至可以使用 JavaScript 在网页端实现二维码扫描功能࿰…...
【人工智能】全景解析:【机器学习】【深度学习】从基础理论到应用前景的【深度探索】
目录 1. 人工智能的基本概念 1.1 人工智能的定义与发展 1.1.1 人工智能的定义 1.1.2 人工智能的发展历史 1.2 人工智能的分类 1.2.1 弱人工智能 1.2.2 强人工智能 1.2.3 超人工智能 1.3 人工智能的关键组成部分 1.3.1 数据 1.3.2 算法 1.3.3 计算能力 2. 机器学习…...
MySQL与PostgreSQL语法区别
1. 数据类型差异 a. 整型 ● MySQL中的text数据类型最大存储容量为64KB,PostgreSQL中的text类型没有此限制。 ● MySQL中使用tinyint、mediumint和int表示不同大小的整数,PostgreSQL使用smallint、int和bigint。 b. 浮点数类型 ● MySQL提供了float和…...
vue2+OpenLayers 天地图上凸显出当前地理位置区域(4)
凸显出当前区域 需要当前地方的json数据 这个可以在阿里的这个阿里 看下效果图 遮盖层的逃命都是可以调的 引入 下面一段代码 import sx from "/views/json/sx1.json"; // 下载的json import GeoJSON from "ol/format/GeoJSON"; // ol的一些方法 imp…...
基于Python、Django开发Web计算器
1、创建项目 创建Django项目参照https://blog.csdn.net/qq_42148307/article/details/140798249,其中项目名为compute,并在该项目下创建一个名为app的应用,并且进行基本的配置。 2、导入Bootstrap前端框架 Bootstrap的使用参照https://blo…...
高性能并行计算面试-核心概念-问题理解
目录 1.什么是并行计算?高性能从哪些方面体现? 2.CPU常见的并行技术 3.GPU并行 4.并发与并行 5.常见的并行计算模型 6.如何评估并行程序的性能? 7.描述Am达尔定律和Gustafson定律,并解释它们对并行计算性能的影响 8.并行计…...
java-activiti笔记
版本:activiti7 <dependency><groupId>org.activiti</groupId><artifactId>activiti-json-converter</artifactId><version>7.0.0.Beta2</version><exclusions><exclusion><groupId>org.mybatis</g…...
Layui——隐藏表单项后不再进行验证
目录 修改后的部分代码 修改后的完整代码 我编辑用户信息和添加新用户用的是同一个表单,不同的是编辑用户信息里没有密码项和确认密码项,但是把它们隐藏后仍然要进行验证,也就是说它们俩的验证并没有随着表单项的隐藏而关闭。原因…...
Github Copilot 使用技巧
🎯目标读者 本文不包含如何安装 Github Copilot本文介绍了 Github Copilot 使用方法和一些技巧 本人已经使用 Github Copilot 2 年了,交了 3 次年费,每年 100$ 着实心痛,但是用着确实爽歪歪 但是感觉一直只用了一小部分功能&am…...
【实现100个unity特效之20】用unity实现物品悬浮和发光像素粒子特效
最终效果 文章目录 最终效果新增飞升粒子效果光圈效果修改不同颜色完结 新增飞升粒子效果 效果 光圈效果 效果 修改不同颜色 完结 赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持…...
GPT-4o mini发布,轻量级大模型如何颠覆AI的未来?
从巨无霸到小巨人:GPT-4o Mini的创新之路 ©作者|潇潇 来源|神州问学 引言 随着人工智能技术的飞速进步,AI领域的竞争日益激烈,大型模型的发布几乎成为常态。然而,这些庞大的模型通常需要大量的计算资源和存储空间ÿ…...
高性能的 C++ Web 开发框架 CPPCMS + WebSocket 模拟实现聊天与文件传输案例。
1. 项目结构 2. config.json {"service": {"api": "http","port": 8080,"ip": "0.0.0.0"},"http": {"script": "","static": "/static"} }3. CMakeLists.txt…...
合合信息OCR支持30类国内常见票据一站式分类识别,支持医疗发票、数电票识别
合合信息TextIn平台明星产品——国内通用票据识别,重磅更新! 产品支持票据类型扩展到23大类、30小类,覆盖场景更全面,同时升级优化了多款票据识别模型,平均识别率较前版本提升11.5%,整体识别速度提升21.9%…...
LeetCode-day40-3151. 特殊数组 I
LeetCode-day40-3151. 特殊数组 I 题目描述示例示例1:示例2:示例3: 思路代码 题目描述 如果数组的每一对相邻元素都是两个奇偶性不同的数字,则该数组被认为是一个 特殊数组 。 Aging 有一个整数数组 nums。如果 nums 是一个 特殊…...
技术研究:Redis 数据结构与 I/O 模型
数据结构 Redis之所以“快”,一方面因为它是内存数据库,所有操作都在内存上完成,内存的访问速度本来就快。另一方面则是因为高效的数据结构,使得操作键值效率较高。总体来说,Redis使用了一个用来保存每个Key/Value的全…...
46-扇孔的处理及铺铜以及布线
1.先连信号线 2.电源管脚,以如下方式处理: 引线打孔处理...
LVS实验的三模式总结
文章目录 LVS的概念叙述NAT工作模式实战案例**思想:**NAT工作模式的优点NAT工作模式的缺点 NAT工作模式的应用场景大致配置 route:打开路由内核功能 部署DR模式集群案例工作思想:大致工作图如下思路模型 具体配置与事实步骤补充 防火墙标签解…...
游戏安全入门-扫雷分析远程线程注入
前言 无论学习什么,首先,我们应该有个目标,那么入门windows游戏安全,脑海中浮现出来的一个游戏 – 扫雷,一款家喻户晓的游戏,虽然已经被大家分析的不能再透了,但是我觉得自己去分析一下还是极好…...
bert-base-chinese模型的完整训练、推理和一些思考
前言 使用google-bert/bert-base-chinese模型进行中文文本分类任务,使用THUCNews中文数据集进行训练,训练完成后,可以导出模型,进行预测。 项目详细介绍和数据下载 数据集下载地址 Github完整代码 现记录训练过程中的一些感悟…...
JS基础5(JS的作用域和JS预解析)
JS的作用域 1. 全局作用域 全局作用域是在代码的任何地方都能访问到的最外层作用域。在浏览器环境下,全局作用域就是window对象,因此所有在全局作用域中声明的变量和函数都会成为window对象的属性和方法。 var globalVar "I am global"; …...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
Linux中INADDR_ANY详解
在Linux网络编程中,INADDR_ANY 是一个特殊的IPv4地址常量(定义在 <netinet/in.h> 头文件中),用于表示绑定到所有可用网络接口的地址。它是服务器程序中的常见用法,允许套接字监听所有本地IP地址上的连接请求。 关…...
以太网PHY布局布线指南
1. 简介 对于以太网布局布线遵循以下准则很重要,因为这将有助于减少信号发射,最大程度地减少噪声,确保器件作用,最大程度地减少泄漏并提高信号质量。 2. PHY设计准则 2.1 DRC错误检查 首先检查DRC规则是否设置正确,然…...
SOC-ESP32S3部分:30-I2S音频-麦克风扬声器驱动
飞书文档https://x509p6c8to.feishu.cn/wiki/SKZzwIRH3i7lsckUOlzcuJsdnVf I2S简介 I2S(Inter-Integrated Circuit Sound)是一种用于传输数字音频数据的通信协议,广泛应用于音频设备中。 ESP32-S3 包含 2 个 I2S 外设,通过配置…...
