Untitled

 avatar
unknown
plain_text
a month ago
7.3 kB
2
Indexable
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, defineEmits } from 'vue';
import { BrowserMultiFormatReader, IScannerControls } from '@zxing/browser';
import { BarcodeFormat } from '@zxing/library';
import { onBeforeRouteLeave } from 'vue-router';

const emit = defineEmits(['code-scanned']);

const videoElement = ref<HTMLVideoElement | null>(null);
let scanner: BrowserMultiFormatReader | null = null;
let currentStream: MediaStream | null = null;
let scannerControls: IScannerControls | null = null;
const scanning = ref(false);

function createScanner() {
  const hints = new Map();
  hints.set('possibleFormats', [
    BarcodeFormat.QR_CODE,
    BarcodeFormat.EAN_13,
    BarcodeFormat.EAN_8,
    BarcodeFormat.CODE_128,
    BarcodeFormat.CODE_39,
    BarcodeFormat.UPC_A,
    BarcodeFormat.UPC_E,
    BarcodeFormat.ISBN
  ]);
  
  return new BrowserMultiFormatReader(hints);
}

async function startScan() {
  console.log('startScan iniziato');

  if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
    alert("Il tuo browser non supporta l'accesso alla fotocamera.");
    return;
  }

  try {
    // Prima fermiamo eventuali scansioni precedenti
    await stopScan();

    // Settiamo scanning a true per far renderizzare il video
    scanning.value = true;
    
    // Aspettiamo il prossimo ciclo di rendering
    await nextTick();
    
    // Aspettiamo un attimo per essere sicuri che il DOM sia aggiornato
    await new Promise(resolve => setTimeout(resolve, 100));

    if (!videoElement.value) {
      throw new Error('Elemento video non trovato dopo il rendering');
    }

    console.log('Video elemento trovato nel DOM');

    // Crea un nuovo scanner
    scanner = createScanner();
    console.log('Scanner creato');

    // Richiedi accesso alla fotocamera
    const stream = await navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: 'environment',
        width: { ideal: 1280 },
        height: { ideal: 720 }
      }
    });

    console.log('Stream ottenuto');

    // Verifica nuovamente che il video esista
    if (!videoElement.value) {
      throw new Error('Elemento video perso durante l\'inizializzazione');
    }
    
    const video = videoElement.value;
    
    // Configura gli event listeners
    const onMetadataLoaded = () => {
      console.log('Video metadata caricati');
    };
    
    const onPlay = () => {
      console.log('Video in riproduzione');
    };
    
    const onError = () => {
      console.error('Errore video:', video.error);
    };

    video.addEventListener('loadedmetadata', onMetadataLoaded);
    video.addEventListener('play', onPlay);
    video.addEventListener('error', onError);

    // Cleanup function per gli event listeners
    const cleanup = () => {
      video.removeEventListener('loadedmetadata', onMetadataLoaded);
      video.removeEventListener('play', onPlay);
      video.removeEventListener('error', onError);
    };

    // Imposta lo stream nel video
    video.srcObject = stream;
    currentStream = stream;

    try {
      await video.play();
      console.log('Video play avviato');

      if (scanner) {
        const deviceId = stream.getVideoTracks()[0].getSettings().deviceId;
        console.log('Avvio scansione con deviceId:', deviceId);
        
        try {
          scannerControls = await scanner.decodeFromVideoDevice(
            deviceId,
            video,
            (result, error) => {
              if (result) {
                const text = result.getText();
                const format = result.getBarcodeFormat();
                console.log("Codice trovato:", text, "Formato:", format);
                
                // Emetti l'evento con il codice scansionato
                emit('code-scanned', text);
                
                cleanup();
                stopScan();
              }
              if (error && error.message !== "No MultiFormat Readers were able to detect the code.") {
                console.warn("Errore durante la scansione:", error);
              }
            }
          );
          console.log('Scanner avviato correttamente');
        } catch (scanError) {
          console.error('Errore durante l\'avvio dello scanner:', scanError);
          throw scanError;
        }
      }
    } catch (playError) {
      cleanup();
      console.error('Errore durante il play del video:', playError);
      throw playError;
    }
  } catch (error) {
    console.error("Errore completo:", error);
    alert("Non è stato possibile accendere la fotocamera. " + error.message);
    scanning.value = false;
  }
}

async function stopScan() {
  console.log('Arresto scansione...');
  try {
    scanning.value = false;
    await nextTick();

    if (scannerControls) {
      console.log('Arresto controlli scanner');
      scannerControls.stop();
      scannerControls = null;
    }

    if (currentStream) {
      currentStream.getTracks().forEach(track => {
        console.log('Arresto track:', track.label);
        track.stop();
      });
      currentStream = null;
    }

    if (videoElement.value) {
      videoElement.value.srcObject = null;
      console.log('Video source rimosso');
    }

    if (scanner) {
      scanner = null;
      console.log('Scanner resettato');
    }

    console.log('Scansione arrestata completamente');
  } catch (error) {
    console.error("Errore durante lo stop dello scanner:", error);
  }
}

// Cleanup quando il componente viene distrutto
onBeforeUnmount(() => {
  stopScan();
});

// Cleanup quando si cambia route
onBeforeRouteLeave((to, from, next) => {
  stopScan();
  next();
});
</script>

<template>
  <div class="scanner-container">
    <div v-if="scanning" class="video-container">
      <video 
        ref="videoElement" 
        playsinline
        autoplay
        muted
        class="scanner-video"
      ></video>
      <div class="scanner-overlay">
        <div class="scanner-frame"></div>
      </div>
      <button @click="stopScan" class="btn btn-danger mt-3">Ferma Scansione</button>
    </div>
    <div v-else class="start-scan">
      <button @click="startScan" class="btn barcode-btn">
        <i class="bi bi-upc-scan fs-1"></i>
      </button>
    </div>
  </div>
</template>

<style scoped>
.scanner-container {
  position: relative;
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
  padding: 20px;
}

.video-container {
  position: relative;
  width: 100%;
  background: #000;
  border-radius: 10px;
  overflow: hidden;
}

.scanner-video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4/3;
  object-fit: cover;
}

.scanner-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.scanner-frame {
  width: 70%;
  height: 70%;
  border: 2px solid #fff;
  border-radius: 10px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.3);
}

.start-scan {
  display: flex;
  justify-content: center;
  padding: 20px;
}

.barcode-btn {
  padding: 15px 30px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 8px;
  transition: all 0.3s ease;
}

.barcode-btn:hover {
  background: #0056b3;
  transform: scale(1.05);
}

button {
  z-index: 10;
  position: relative;
}
</style>
Leave a Comment