前端黑科技:使用 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"; …...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
