扫码方式:使用Html5Qrcode库
1.npm install html5-qrcode
2.import { Html5Qrcode } from "html5-qrcode"; // 引入 H5 扫码库
3-1相机扫码的vue弹窗代码
vue
复制代码
<u-overlay :show="scanShow" @click="scanShow = false" opacity="0.6">
<view class="warp">
<view class="rect">
<view id="sreader" class="sreader" style="width: 100%; height: 100%">
</view>
</view>
<div class="scan-wave" :class="{ active: cameraReady }"></div>
<view class="btn">
<u-button
color="#AB004F"
type="primary"
class="next-btn"
@click="stopScan"
>停止扫码</u-button
>
</view>
</view>
</u-overlay>
3-2选择图片扫码
js
复制代码
<view id="reader" class="reader" style="display: none"></view>
4.点击按钮选择扫码方式
js
复制代码
chooseScanWay() {
uni.showActionSheet({
itemList: ["相机扫码", "选择图片扫码"],
success: (res) => {
if (res.tapIndex === 0) {
// 选择相机扫码
console.log("1");
this.scanShow = true;
this.scan();
} else if (res.tapIndex === 1) {
// 选择图片扫码
console.log(2);
this.h5AlbumScan();
// this.openAlbumScan();
}
},
fail: (err) => {
console.log("取消选择:", err);
},
});
},
5-1 选择图片扫码
js
复制代码
h5AlbumScan() {
// 创建隐藏的文件选择框(模拟点击)
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/*"; // 仅允许选择图片
fileInput.style.display = "none";
// 监听文件选择事件
fileInput.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const htmlCode = new Html5Qrcode("reader");
// 解码图片中的二维码
htmlCode
.scanFile(file)
.then((decodedText) => {
console.log(decodedText, "decodedText");
uni.showToast({ title: "扫码成功" + decodedText, icon: "success" });
})
.catch((err) => {
uni.showToast({ title: "图片中未识别到二维码", icon: "none" });
console.error("图片解码失败", err);
});
// 移除临时文件选择框
document.body.removeChild(fileInput);
};
// 添加到页面并触发点击
document.body.appendChild(fileInput);
fileInput.click();
},
5-2 相机扫码方法
js
复制代码
scan() {
// 定义容器尺寸(500rpx × 500rpx)
const containerRpx = 500;
// 转换rpx为px(核心:uniapp rpx适配公式)
const pxPerRpx = window.innerWidth / 750;
const containerPx = containerRpx * pxPerRpx; // 500rpx对应的实际px值
const height = window.innerHeight;
const width = window.innerWidth;
const codeContent = "1024";
Html5Qrcode.getCameras()
.then((devices) => {
// console.log(devices)
this.html5QrCode = new Html5Qrcode("sreader");
const aspectRatio = height / width;
if (devices && devices.length) {
// 相机启动成功后,延迟显示动画
setTimeout(() => {
this.cameraReady = true;
}, 0);
this.html5QrCode
.start(
{ facingMode: { exact: "environment" } },
{
fps: 10, // 提升帧率,不影响尺寸,仅优化识别
aspectRatio: aspectRatio, // 强制和扫码区域同比例,避免拉伸
// qrbox适配扫码区域(设为区域的80%,避免超出)
qrbox: {
width: width * 1,
height: height * 1,
},
videoConstraints: {
facingMode: "environment",
// 强制视频流尺寸不超过扫码区域,避免溢出
// width: { ideal: width, max: width },
// height: { ideal: height, max: height },
aspectRatio: aspectRatio, // 锁定比例,杜绝拉伸溢出
},
// 保留条形码识别优化(无兼容问题)
disableAutoFocus: true, // 开启自动对焦,画面清晰
tryHarder: true, // 深度识别,兼容条形码
},
(decodedText, decodedResult) => {
// do something when code is read
console.log("decodedText", decodedText);
uni.showToast({
title: decodedText,
icon: "none",
});
// console.log('decodedResult', decodedResult)
},
(errorMessage) => {
// parse error, ignore it.
// console.log('parse error, ignore it.', errorMessage)
},
)
.catch((err) => {
// Start failed, handle it.
console.log("Start failed, handle it.");
});
}
})
.catch((err) => {
// handle err
console.log(err);
uni.showToast({
title: err,
icon: "none",
});
});
},
6.扫码完成需要关闭摄像头
js
复制代码
stopScan() {
if (this.html5QrCode) {
this.html5QrCode
.stop()
.then(() => {
console.log("摄像头已关闭");
this.scanShow = false;
this.html5QrCode = null;
})
.catch((err) => {
console.error("关闭摄像头失败", err);
});
} else {
this.scanShow = false;
}
},
7.扫码模块样式
css
复制代码
.warp {
display: flex;
align-items: center;
// justify-content: center;
// padding-top: 50%;
height: 100vh;
flex-direction: column;
}
.btn {
// padding-top: 100rpx;
position: absolute;
bottom: 100rpx;
}
.rect {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
// background-color: #fff;
// background:
// /* 左上角:横向 + 纵向 */
// linear-gradient(#007aff, #007aff) 0 0 / 40rpx 10rpx no-repeat,
// linear-gradient(#007aff, #007aff) 0 0 / 10rpx 40rpx no-repeat,
// /* 右上角:横向 + 纵向 */
// linear-gradient(#007aff, #007aff) 100% 0 / 40rpx 10rpx no-repeat,
// linear-gradient(#007aff, #007aff) 100% 0 / 10rpx 40rpx no-repeat,
// /* 左下角:横向 + 纵向 */
// linear-gradient(#007aff, #007aff) 0 100% / 40rpx 10rpx no-repeat,
// linear-gradient(#007aff, #007aff) 0 100% / 10rpx 40rpx no-repeat,
// /* 右下角:横向 + 纵向 */
// linear-gradient(#007aff, #007aff) 100% 100% / 40rpx 10rpx no-repeat,
// linear-gradient(#007aff, #007aff) 100% 100% / 10rpx 40rpx no-repeat;
// background-color: #fff; /* 基础背景色 */
background-color: transparent;
.sreader {
position: absolute;
}
}
/* 扫描波纹/扫描线样式 */
.scan-wave {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 1rpx; /* 扫描线粗细 */
// will-change: transform;
// backface-visibility: hidden;
transform: translateZ(0);
background: linear-gradient(
90deg,
transparent,
#07c160,
/* 微信绿 */ #07c160,
transparent
);
border-radius: 50%;
/* 微信风格光晕:柔和的绿色光晕 */
box-shadow: 0 0 30rpx 10rpx rgba(7, 193, 96, 0.3);
pointer-events: none; /* 避免遮挡交互 */
z-index: 2; /* 层级高于视频,低于角标 */
/* 核心动画:从顶部滚动到底部 */
animation: scanMove 3s linear infinite;
animation-play-state: paused; /* 默认暂停 */
}
.scan-wave.active {
animation-play-state: running; /* 相机就绪后播放 */
}
/* 扫描线滚动动画 */
@keyframes scanMove {
0% {
top: 0; /* 顶部起始位置 */
opacity: 1; /* 初始完全不透明 */
}
85% {
top: 85%; /* 接近底部 */
opacity: 1;
}
100% {
top: 100%; /* 停在底部 */
opacity: 1;
}
}