<template>
	<view>
		<view class="scan-container" v-show="showSM" @tap="closeScan">
			<video id="video-node" ref="videoNode" style="position: absolute; left: -9999px; width: 100px; height: 100px;"
				muted autoplay playsinline="true" webkit-playsinline="true"></video>
		
			<view class="display-wrapper">
				<canvas canvas-id="displayCanvas" id="displayCanvas" class="display-canvas"></canvas>
		
				<view class="scan-mask">
					<view class="scan-box">
						<view class="line"></view>
						<view class="corner top-left"></view>
						<view class="corner top-right"></view>
						<view class="corner bottom-left"></view>
						<view class="corner bottom-right"></view>
					</view>
					<view class="tip" v-if="isScanning">正在扫描二维码...</view>
					<button v-else class="start-btn" @tap="initScanner">点击开启摄像头</button>
				</view>
			</view>
			<view class="bottom-bar">
				<button class="action-btn" @tap="closeScan">返回</button>
			</view>
		</view>
	</view>
</template>

<script>
	// 请确保已安装 jsQR: npm install jsqr
	import jsQR from 'jsqr';

	export default {
		data() {
			return {
				showSM: false,
				videoElement: null,
				canvasContext: null,
				isScanning: false,
				videoStream: null,
				timer: null
			};
		},
		onReady() {
			// 页面准备好后先尝试初始化，如果被浏览器拦截，用户点击按钮可再次触发
			// this.initScanner();
		},
		methods: {
			/**
			 * 初始化扫描器
			 */
			async initScanner() {
				if (this.isScanning) return;

				try {
					// 1. 获取原生 DOM 节点
					// 在 UniApp H5 中，通过 document.querySelector 寻找原生 video 标签最稳妥
					const video = document.querySelector('#video-node video') || document.getElementById('video-node');
					this.videoElement = video;
					
					if (!this.videoElement || typeof this.videoElement.play !== 'function') {
						// 针对部分 UniApp 渲染差异的兼容处理
						this.videoElement = video.getElementsByTagName('video')[0] || video;
					}
					
					// 2. 获取摄像头权限
					const stream = await navigator.mediaDevices.getUserMedia({
						video: {
							facingMode: "environment", // 优先后置
							width: {
								ideal: 750
							},
							height: {
								ideal: 1280
							}
						}
					});
					
					this.videoStream = stream;
					this.videoElement.srcObject = stream;
					
					// 3. 确保视频播放
					await this.videoElement.play();
					
					
					// 4. 初始化绘图上下文
					this.canvasContext = document.createElement('canvas').getContext('2d', {
						willReadFrequently: true
					});

					this.isScanning = true;
					
					this.renderLoop();

					console.log("摄像头启动成功");
				} catch (err) {
					console.error("启动失败详情:", err);
					uni.showToast({
						title: '请在HTTPS环境下点击开启',
						icon: 'none',
						duration: 3000
					});
				}
			},

			/**
			 * 渲染与解码主循环
			 */
			renderLoop() {
				
				if (!this.isScanning) return;
				
				// HAVE_ENOUGH_DATA 表示视频帧已就绪
				if (this.videoElement.readyState === this.videoElement.HAVE_ENOUGH_DATA) {
					
					
					const vw = this.videoElement.videoWidth;
					const vh = this.videoElement.videoHeight;

					// 绘制到可见的 Canvas 上
					this.drawToDisplay(vw, vh);
					
					
					// 执行解码
					this.decode(vw, vh);
				}

				// 使用 requestAnimationFrame 保持 60fps 渲染
				this.timer = requestAnimationFrame(this.renderLoop);
			},

			/**
			 * 绘制可见画面
			 */
			drawToDisplay(vw, vh) {
				const canvas = document.querySelector('#displayCanvas canvas');
				if (!canvas) return;

				const ctx = canvas.getContext('2d');
				const dpr = window.devicePixelRatio || 1;
				const screenW = window.innerWidth;
				const screenH = window.innerHeight;

				canvas.width = screenW * dpr;
				canvas.height = screenH * dpr;

				// 计算 Object-Fit: Cover
				const scale = Math.max(canvas.width / vw, canvas.height / vh);
				const x = (canvas.width / 1.5) - (vw / 1.5) * scale;
				const y = (canvas.height / 1.5) - (vh / 1.5) * scale;

				ctx.drawImage(this.videoElement, x, y, vw * scale, vh * scale);
			},

			/**
			 * jsQR 识别逻辑
			 */
			decode(vw, vh) {
				
				// 降低解码频率可提升低端机性能，这里我们全速运行，若卡顿可加计数器
				this.canvasContext.canvas.width = vw;
				this.canvasContext.canvas.height = vh;
				this.canvasContext.drawImage(this.videoElement, 0, 0, vw, vh);

				const imageData = this.canvasContext.getImageData(0, 0, vw, vh);
				const code = jsQR(imageData.data, imageData.width, imageData.height, {
					inversionAttempts: "dontInvert",
				});
				// console.log(code,"aaa");
				if (code && code.data) {
					this.stopScanning();
					this.handleSuccess(code.data);
				}
			},

			handleSuccess(result) {
				uni.vibrateShort();
				uni.showModal({
					title: '扫码结果',
					content: result,
					confirmText: '确定',
					showCancel: false,
					success: () => {
						this.$emit('scanSuccess', result);
						this.showSM = false;
						this.stopScanning();
						// uni.navigateBack();
					}
				});
			},

			stopScanning() {
				this.isScanning = false;
				if (this.timer) cancelAnimationFrame(this.timer);
				if (this.videoStream) {
					this.videoStream.getTracks().forEach(track => track.stop());
				}
			},
			showScan() {
				this.showSM = true;
				// 开始扫码
				this.initScanner();
			},
			closeScan() {
				this.showSM = false;
				this.stopScanning();
				// uni.navigateBack();
			}
		},
		beforeDestroy() {
			this.stopScanning();
		}
	};
</script>

<style scoped>
	.scan-container {
		position: fixed;
		inset: 0;
		background: #000;
		z-index: 999;
	}

	.display-wrapper {
		position: relative;
		width: 100vw;
		height: 100vh;
	}

	.display-canvas {
		width: 100%;
		height: 100%;
	}

	/* 装饰层 */
	.scan-mask {
		position: absolute;
		inset: 0;
		background: rgba(0, 0, 0, 0.4);
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.scan-box {
		position: relative;
		width: 480rpx;
		height: 480rpx;
		box-shadow: 0 0 0 1000px rgba(0, 0, 0, 0.4);
		/* 形成中间镂空感 */
		background: transparent;
	}

	.corner {
		position: absolute;
		width: 30rpx;
		height: 30rpx;
		border: 6rpx solid #007aff;
	}

	.top-left {
		top: 0;
		left: 0;
		border-right: none;
		border-bottom: none;
	}

	.top-right {
		top: 0;
		right: 0;
		border-left: none;
		border-bottom: none;
	}

	.bottom-left {
		bottom: 0;
		left: 0;
		border-right: none;
		border-top: none;
	}

	.bottom-right {
		bottom: 0;
		right: 0;
		border-left: none;
		border-top: none;
	}

	.line {
		position: absolute;
		width: 100%;
		height: 2px;
		background: linear-gradient(to right, transparent, #007aff, transparent);
		animation: scanMove 2s infinite linear;
	}

	@keyframes scanMove {
		0% {
			top: 0;
		}

		100% {
			top: 100%;
		}
	}

	.tip {
		margin-top: 80rpx;
		color: #fff;
		font-size: 28rpx;
	}

	.start-btn {
		margin-top: 80rpx;
		background: #007aff;
		color: #fff;
		border-radius: 40rpx;
		font-size: 28rpx;
		padding: 0 40rpx;
	}

	.bottom-bar {
		position: absolute;
		bottom: 100rpx;
		width: 100%;
		display: flex;
		justify-content: center;
	}

	.action-btn {
		background: rgba(255, 255, 255, 0.2);
		color: #fff;
		border: 1px solid #fff;
		font-size: 26rpx;
		border-radius: 40rpx;
	}
</style>