You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular 10中无需输入框实现USB扫码枪持续扫码的方案咨询

解决Angular 10中无需输入框捕获USB扫码枪(键盘模式)输入的方案

我之前做Angular项目时刚好解决过这个问题——键盘模式的扫码枪本质就是模拟快速的键盘输入序列,咱们完全可以自己实现全局监听,不需要依赖第三方包。下面是具体的实现步骤:

1. 创建全局扫码监听服务

因为要在整个应用范围内捕获扫码输入,用Angular服务来封装逻辑最合适,这样任何组件都能订阅扫码结果。

import { Injectable, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class BarcodeScannerService implements OnDestroy {
  // 用于向组件推送扫码结果的Subject
  private barcodeSubject = new Subject<string>();
  // 存储扫码过程中输入的字符
  private inputBuffer = '';
  // 计时器ID,用于区分手动输入和扫码(扫码输入间隔极短)
  private timeoutId: number | null = null;
  // 扫码输入的最大间隔时间,可根据实际扫码枪调整(一般300ms足够)
  private readonly SCAN_INTERVAL = 300;

  constructor() {
    // 全局监听document的keydown事件
    document.addEventListener('keydown', this.handleKeyDown.bind(this));
  }

  // 供组件订阅的扫码结果Observable
  get barcode$() {
    return this.barcodeSubject.asObservable();
  }

  private handleKeyDown(event: KeyboardEvent): void {
    // 忽略功能键(Shift、Ctrl、Alt等),只处理普通字符和回车键
    if (event.key.length > 1 && event.key !== 'Enter') {
      return;
    }

    // 回车键表示扫码结束(大部分扫码枪会自动追加回车)
    if (event.key === 'Enter') {
      if (this.inputBuffer.length > 0) {
        this.barcodeSubject.next(this.inputBuffer);
        this.resetInputState();
      }
      event.preventDefault(); // 阻止默认回车行为(比如页面跳转、表单提交)
      return;
    }

    // 收集普通输入字符
    this.inputBuffer += event.key;

    // 清除之前的计时器,重新计时
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }

    // 如果超过设定时间没有新输入,判定为手动输入,清空缓冲区
    this.timeoutId = window.setTimeout(() => {
      this.resetInputState();
    }, this.SCAN_INTERVAL);
  }

  // 重置输入状态
  private resetInputState(): void {
    this.inputBuffer = '';
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = null;
    }
  }

  // 组件销毁时移除监听,避免内存泄漏
  ngOnDestroy(): void {
    document.removeEventListener('keydown', this.handleKeyDown.bind(this));
    this.barcodeSubject.complete();
  }
}

2. 在组件中使用服务订阅扫码结果

随便找一个组件(比如根组件),注入服务并订阅扫码结果,就能处理扫码数据了:

import { Component, OnInit } from '@angular/core';
import { BarcodeScannerService } from './barcode-scanner.service';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h2>扫码枪监听中...</h2>
      <p>最新扫码结果: {{ scannedBarcode }}</p>
    </div>
  `
})
export class AppComponent implements OnInit {
  scannedBarcode = '';

  constructor(private barcodeScannerService: BarcodeScannerService) {}

  ngOnInit(): void {
    // 订阅扫码结果
    this.barcodeScannerService.barcode$.subscribe(barcode => {
      this.scannedBarcode = barcode;
      // 这里可以添加你的业务逻辑,比如调用API、跳转页面等
      console.log('捕获到条码:', barcode);
    });
  }
}

3. 注意事项和优化点

  • 扫码枪输出差异:部分扫码枪可能不会自动追加回车键,这时候可以根据条码的固定长度来判断(比如EAN-13是13位),或者在扫码枪设置里开启“追加回车”功能。
  • 输入框冲突处理:如果页面有输入框,需要在输入框聚焦时暂停全局监听,避免扫码输入被同时捕获。可以在服务里添加pause()resume()方法来控制监听状态。
  • 调整间隔时间:如果你的扫码枪输入速度较慢,可以适当调大SCAN_INTERVAL的数值,确保不会提前清空缓冲区。
  • 内存泄漏防范:服务实现了OnDestroy接口,移除了事件监听,确保组件销毁时不会留下内存泄漏。

内容的提问来源于stack exchange,提问作者Jakub

火山引擎 最新活动