Untitled
unknown
plain_text
10 months ago
8.2 kB
7
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 stop-btn">
<i class="bi bi-x-lg"></i> Ferma Scansione
</button>
</div>
<div v-else class="scan-button-container">
<button @click="startScan" class="btn scan-btn" title="Scansiona codice">
<i class="bi bi-upc-scan"></i>
</button>
</div>
</div>
</template>
<style scoped>
.scanner-container {
display: inline-flex;
align-items: center;
}
.scan-button-container {
height: 100%;
display: flex;
align-items: center;
}
.scan-btn {
height: 42px;
width: 50px;
padding: 0;
margin-left: 8px;
background-color: rgb(253, 148, 68);
color: white;
border: none;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.scan-btn i {
font-size: 1.5rem;
}
.scan-btn:hover {
background-color: rgb(233, 128, 48);
transform: translateY(-1px);
}
.scan-btn:active {
transform: translateY(1px);
}
.video-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.scanner-video {
max-width: 100%;
max-height: 70vh;
border-radius: 12px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
.scanner-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
}
.scanner-frame {
width: 280px;
height: 280px;
border: 3px solid rgb(253, 148, 68);
border-radius: 12px;
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.5);
position: relative;
}
.scanner-frame::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 2px;
background-color: rgb(253, 148, 68);
animation: scan 2s linear infinite;
}
@keyframes scan {
0% {
transform: translateY(-100px);
}
100% {
transform: translateY(100px);
}
}
.stop-btn {
margin-top: 20px;
padding: 10px 20px;
font-size: 1.1rem;
border-radius: 8px;
display: flex;
align-items: center;
gap: 8px;
}
.stop-btn i {
font-size: 1.2rem;
}
</style>
Editor is loading...
Leave a Comment