React实时视频识别代码转Angular组件的步骤及处理咨询
React实时视频识别功能迁移到Angular的完整步骤
首先明确:不能直接将JSX文件改扩展名或复制代码到Angular TS组件中。React和Angular的组件模型、生命周期、模板语法完全不同,必须按Angular的架构重构代码。
一、前置依赖安装
在Angular项目根目录执行命令,安装Tesseract.js及类型定义:
npm install tesseract.js npm install @types/tesseract.js --save-dev
二、生成Angular组件
创建专门的视频扫描组件:
ng generate component video-scanner
三、重构核心功能
1. 模板文件(video-scanner.component.html)
将React的JSX转为Angular模板语法,使用属性绑定处理样式和元素引用:
<div> <video #myVideo autoplay muted playsinline [style.width.px]="vidWidth" [style.height.px]="vidHeight" [style.position]="'absolute'" [style.top.px]="vidOffsetTop" [style.left.px]="vidOffsetLeft" [style.zIndex]="2" ></video> <div [style.width.px]="indWidth" [style.height.px]="indHeight" [style.border]="'1px solid red'" [style.zIndex]="3" [style.position]="'absolute'" [style.top.px]="indOffsetTop" [style.left.px]="indOffsetLeft" ></div> </div>
2. 组件类文件(video-scanner.component.ts)
用Angular的生命周期钩子、@ViewChild替代React的useEffect和useRef,重构业务逻辑:
import { Component, ViewChild, ElementRef, OnInit, OnDestroy, Output, EventEmitter } from '@angular/core'; import { createWorker } from 'tesseract.js'; @Component({ selector: 'app-video-scanner', templateUrl: './video-scanner.component.html', styleUrls: ['./video-scanner.component.css'] }) export class VideoScannerComponent implements OnInit, OnDestroy { @ViewChild('myVideo') myVideo!: ElementRef<HTMLVideoElement>; @Output() codeChange = new EventEmitter<string>(); @Output() scanningChange = new EventEmitter<boolean>(); private myStream?: MediaStream; private scannedCodes: Record<string, boolean> = {}; private worker = createWorker(); private animationId?: number; // 视频配置参数 vidWidth = window.innerWidth - 60; vidHeight = 260; vidOffsetTop = 240; vidOffsetLeft = (window.innerWidth / 3) - (this.vidWidth / 2); // 识别框配置参数 marginX = 40; indWidth = this.vidWidth - this.marginX; indHeight = 80; indOffsetTop = this.vidOffsetTop + (this.vidHeight / 2) - (this.indHeight / 2); indOffsetLeft = (window.innerWidth / 2) - (this.indWidth / 2); ngOnInit(): void { this.initCameraAndOCR(); } ngOnDestroy(): void { // 清理资源,避免内存泄漏 if (this.myStream) { this.myStream.getTracks().forEach(track => track.stop()); } if (this.animationId) { cancelAnimationFrame(this.animationId); } this.worker.terminate(); } private async initCameraAndOCR(): Promise<void> { try { this.scanningChange.emit(true); // 请求摄像头权限 const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false }); // 绑定视频流 const videoElement = this.myVideo.nativeElement; videoElement.srcObject = stream; await videoElement.play(); this.myStream = stream; this.scannedCodes = {}; // 初始化Tesseract OCR worker await this.worker.load(); await this.worker.loadLanguage('eng'); await this.worker.initialize('eng'); // 启动帧识别循环 this.tick(); } catch (err) { console.error(err); this.scanningChange.emit(false); // 这里可添加错误提示逻辑,比如弹窗 } } private async tick(): Promise<void> { const videoElement = this.myVideo.nativeElement; if (videoElement.readyState === videoElement.HAVE_ENOUGH_DATA) { // 创建Canvas处理视频帧 const canvas = document.createElement('canvas'); canvas.width = this.indWidth; canvas.height = this.indHeight; const ctx = canvas.getContext('2d'); if (ctx) { // 截取视频中指定区域绘制到Canvas const sx = (this.marginX / 2) / 2; const sy = this.vidHeight - this.indHeight; const sWidth = this.indWidth * 2; const sHeight = this.indHeight * 2; const dx = 0; const dy = 0; const dWidth = this.indWidth; const dHeight = this.indHeight; ctx.drawImage(videoElement, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); } // OCR识别 const { data: { text } } = await this.worker.recognize(canvas); const regex = /[a-zA-Z0-9]/gi; const scannedText = text?.match(regex)?.filter(x => x).join('') || ''; if (scannedText) { this.codeChange.emit(scannedText); } console.log({ text, scannedText }); } // 循环处理下一帧 this.animationId = requestAnimationFrame(() => this.tick()); } }
3. 样式文件(video-scanner.component.css)
可留空或补充全局样式,模板中已通过绑定实现了原React的内联样式逻辑。
四、父组件使用
在父组件模板中引入扫描组件,并绑定回调事件:
<app-video-scanner (codeChange)="handleCodeUpdate($event)" (scanningChange)="handleScanningStatus($event)" ></app-video-scanner>
在父组件类中实现对应的处理方法:
handleCodeUpdate(code: string): void { // 处理识别到的代码,对应React的setCode } handleScanningStatus(isScanning: boolean): void { // 处理扫描状态,对应React的setScanning }
内容的提问来源于stack exchange,提问作者Ziad El Khomssi
相关产品推荐
相关产品推荐

