当前位置: 首页 > news >正文

前端黑科技:使用 JavaScript 实现网页扫码功能

在数字化时代,二维码已经渗透到我们生活的方方面面。从移动支付到产品溯源,二维码凭借其便捷性和高效性,成为了信息传递的重要载体。而随着前端技术的不断发展,我们甚至可以使用 JavaScript 在网页端实现二维码扫描功能,为用户提供更加便捷的操作体验。

本文将带您深入了解如何使用 JavaScript 调用摄像头,结合 jsQR 库,以及如何控制闪光灯,最终实现一个功能完善的网页扫码应用。

一、 项目概述

我们将创建一个简单的网页应用,该应用能够:

  1. 调用设备摄像头,获取实时视频流。
  2. 在网页上创建一个扫描区域,用户可以将二维码放置在该区域内进行扫描。
  3. 使用 jsQR 库解码扫描区域内的二维码图像数据,获取二维码内容。
  4. 提供手动输入二维码内容的功能。
  5. 如果设备支持,还可以控制闪光灯的开关,以便在光线不足的情况下进行扫描。

二、 实现步骤

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 代码是实现扫码功能的核心部分,主要包括以下几个步骤:

  1. 获取摄像头权限: 使用 navigator.mediaDevices.getUserMedia() 方法请求访问用户的摄像头。

  2. 播放视频流: 将获取到的视频流赋值给 <video> 标签的 srcObject 属性,并调用 video.play() 方法开始播放。

  3. 创建扫描循环: 使用 requestAnimationFrame() 方法创建一个循环,不断地从视频流中获取帧图像,并进行二维码解码。

  4. 绘制视频帧: 在每一帧中,使用 canvas.drawImage() 方法将视频帧绘制到 <canvas> 元素上。

  5. 获取扫描区域图像数据: 使用 canvas.getImageData() 方法获取扫描区域的图像数据。

  6. 解码二维码: 使用 jsQR 库的 jsQR() 方法解码图像数据,如果解码成功,则获取二维码内容。

  7. 处理扫描结果: 对解码后的二维码内容进行处理,例如跳转到链接、显示信息等。

  8. 实现其他功能: 实现手动输入二维码内容和控制闪光灯等功能。

// 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 实现网页扫码功能

在数字化时代&#xff0c;二维码已经渗透到我们生活的方方面面。从移动支付到产品溯源&#xff0c;二维码凭借其便捷性和高效性&#xff0c;成为了信息传递的重要载体。而随着前端技术的不断发展&#xff0c;我们甚至可以使用 JavaScript 在网页端实现二维码扫描功能&#xff0…...

【人工智能】全景解析:【机器学习】【深度学习】从基础理论到应用前景的【深度探索】

目录 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&#xff0c;PostgreSQL中的text类型没有此限制。 ● MySQL中使用tinyint、mediumint和int表示不同大小的整数&#xff0c;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&#xff0c;其中项目名为compute&#xff0c;并在该项目下创建一个名为app的应用&#xff0c;并且进行基本的配置。 2、导入Bootstrap前端框架 Bootstrap的使用参照https://blo…...

高性能并行计算面试-核心概念-问题理解

目录 1.什么是并行计算&#xff1f;高性能从哪些方面体现&#xff1f; 2.CPU常见的并行技术 3.GPU并行 4.并发与并行 5.常见的并行计算模型 6.如何评估并行程序的性能&#xff1f; 7.描述Am达尔定律和Gustafson定律&#xff0c;并解释它们对并行计算性能的影响 8.并行计…...

java-activiti笔记

版本&#xff1a;activiti7 <dependency><groupId>org.activiti</groupId><artifactId>activiti-json-converter</artifactId><version>7.0.0.Beta2</version><exclusions><exclusion><groupId>org.mybatis</g…...

Layui——隐藏表单项后不再进行验证

目录 修改后的部分代码 修改后的完整代码 我编辑用户信息和添加新用户用的是同一个表单&#xff0c;不同的是编辑用户信息里没有密码项和确认密码项&#xff0c;但是把它们隐藏后仍然要进行验证&#xff0c;也就是说它们俩的验证并没有随着表单项的隐藏而关闭。原因&#xf…...

Github Copilot 使用技巧

&#x1f3af;目标读者 本文不包含如何安装 Github Copilot本文介绍了 Github Copilot 使用方法和一些技巧 本人已经使用 Github Copilot 2 年了&#xff0c;交了 3 次年费&#xff0c;每年 100$ 着实心痛&#xff0c;但是用着确实爽歪歪 但是感觉一直只用了一小部分功能&am…...

【实现100个unity特效之20】用unity实现物品悬浮和发光像素粒子特效

最终效果 文章目录 最终效果新增飞升粒子效果光圈效果修改不同颜色完结 新增飞升粒子效果 效果 光圈效果 效果 修改不同颜色 完结 赠人玫瑰&#xff0c;手有余香&#xff01;如果文章内容对你有所帮助&#xff0c;请不要吝啬你的点赞评论和关注&#xff0c;你的每一次支持…...

GPT-4o mini发布,轻量级大模型如何颠覆AI的未来?

从巨无霸到小巨人&#xff1a;GPT-4o Mini的创新之路 ©作者|潇潇 来源|神州问学 引言 随着人工智能技术的飞速进步&#xff0c;AI领域的竞争日益激烈&#xff0c;大型模型的发布几乎成为常态。然而&#xff0c;这些庞大的模型通常需要大量的计算资源和存储空间&#xff…...

高性能的 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平台明星产品——国内通用票据识别&#xff0c;重磅更新&#xff01; 产品支持票据类型扩展到23大类、30小类&#xff0c;覆盖场景更全面&#xff0c;同时升级优化了多款票据识别模型&#xff0c;平均识别率较前版本提升11.5%&#xff0c;整体识别速度提升21.9%…...

LeetCode-day40-3151. 特殊数组 I

LeetCode-day40-3151. 特殊数组 I 题目描述示例示例1&#xff1a;示例2&#xff1a;示例3&#xff1a; 思路代码 题目描述 如果数组的每一对相邻元素都是两个奇偶性不同的数字&#xff0c;则该数组被认为是一个 特殊数组 。 Aging 有一个整数数组 nums。如果 nums 是一个 特殊…...

技术研究:Redis 数据结构与 I/O 模型

数据结构 Redis之所以“快”&#xff0c;一方面因为它是内存数据库&#xff0c;所有操作都在内存上完成&#xff0c;内存的访问速度本来就快。另一方面则是因为高效的数据结构&#xff0c;使得操作键值效率较高。总体来说&#xff0c;Redis使用了一个用来保存每个Key/Value的全…...

46-扇孔的处理及铺铜以及布线

1.先连信号线 2.电源管脚,以如下方式处理&#xff1a; 引线打孔处理...

LVS实验的三模式总结

文章目录 LVS的概念叙述NAT工作模式实战案例**思想&#xff1a;**NAT工作模式的优点NAT工作模式的缺点 NAT工作模式的应用场景大致配置 route&#xff1a;打开路由内核功能 部署DR模式集群案例工作思想&#xff1a;大致工作图如下思路模型 具体配置与事实步骤补充 防火墙标签解…...

游戏安全入门-扫雷分析远程线程注入

前言 无论学习什么&#xff0c;首先&#xff0c;我们应该有个目标&#xff0c;那么入门windows游戏安全&#xff0c;脑海中浮现出来的一个游戏 – 扫雷&#xff0c;一款家喻户晓的游戏&#xff0c;虽然已经被大家分析的不能再透了&#xff0c;但是我觉得自己去分析一下还是极好…...

bert-base-chinese模型的完整训练、推理和一些思考

前言 使用google-bert/bert-base-chinese模型进行中文文本分类任务&#xff0c;使用THUCNews中文数据集进行训练&#xff0c;训练完成后&#xff0c;可以导出模型&#xff0c;进行预测。 项目详细介绍和数据下载 数据集下载地址 Github完整代码 现记录训练过程中的一些感悟…...

JS基础5(JS的作用域和JS预解析)

JS的作用域 1. 全局作用域 全局作用域是在代码的任何地方都能访问到的最外层作用域。在浏览器环境下&#xff0c;全局作用域就是window对象&#xff0c;因此所有在全局作用域中声明的变量和函数都会成为window对象的属性和方法。 var globalVar "I am global"; …...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...