Manager
View Site
Name
Type
React (.jsx)
HTML (.html)
Icon
Description
Code Editor
Revision History (0)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Mobile Barcode Scanner</title> <!-- Tailwind CSS for styling --> <script src="https://cdn.tailwindcss.com"></script> <!-- HTML5-QRCode Library for scanning logic --> <script src="https://unpkg.com/html5-qrcode"></script> <style> /* Hide the default injected styling from the library */ #reader { border: none !important; } #reader video { object-fit: cover !important; border-radius: 0.5rem; } </style> </head> <body class="bg-gray-100 flex items-center justify-center min-h-screen font-sans"> <!-- Mobile-sized container --> <div class="w-full h-screen sm:h-[800px] sm:max-w-md sm:rounded-3xl sm:border-4 border-gray-800 bg-white shadow-2xl flex flex-col relative overflow-hidden"> <!-- App Header --> <header class="bg-indigo-600 text-white p-5 shadow-md z-10 flex items-center justify-between"> <div class="flex items-center gap-3"> <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M3 7V5a2 2 0 0 1 2-2h2"></path> <path d="M17 3h2a2 2 0 0 1 2 2v2"></path> <path d="M21 17v2a2 2 0 0 1-2 2h-2"></path> <path d="M7 21H5a2 2 0 0 1-2-2v-2"></path> <line x1="7" y1="12" x2="17" y2="12"></line> </svg> <h1 class="text-xl font-bold tracking-wide">Scanner Pro</h1> </div> <div id="status-indicator" class="w-3 h-3 rounded-full bg-red-400 animate-pulse"></div> </header> <!-- Camera Area --> <div class="flex-1 bg-black relative flex items-center justify-center p-4"> <!-- Video feed injects here --> <div id="reader" class="w-full h-full relative z-10 hidden"></div> <!-- Placeholder when camera is off --> <div id="camera-placeholder" class="absolute inset-0 flex flex-col items-center justify-center text-gray-500 z-0"> <svg xmlns="http://www.w3.org/2000/svg" class="w-16 h-16 mb-4 opacity-50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"></path> <circle cx="12" cy="13" r="3"></circle> </svg> <p class="text-sm font-medium">Camera is currently inactive</p> <p class="text-xs mt-1 opacity-70">Tap 'Start Scanning' below</p> </div> <!-- Custom Scanning Overlay Grid --> <div id="scan-overlay" class="absolute inset-0 z-20 pointer-events-none hidden flex items-center justify-center"> <div class="w-64 h-64 border-2 border-indigo-500/50 rounded-xl relative"> <div class="absolute top-0 left-0 w-8 h-8 border-t-4 border-l-4 border-indigo-500 rounded-tl-xl"></div> <div class="absolute top-0 right-0 w-8 h-8 border-t-4 border-r-4 border-indigo-500 rounded-tr-xl"></div> <div class="absolute bottom-0 left-0 w-8 h-8 border-b-4 border-l-4 border-indigo-500 rounded-bl-xl"></div> <div class="absolute bottom-0 right-0 w-8 h-8 border-b-4 border-r-4 border-indigo-500 rounded-br-xl"></div> <div class="absolute top-1/2 left-0 w-full h-[1px] bg-red-500/70 shadow-[0_0_8px_rgba(239,68,68,0.8)] scanner-laser"></div> </div> </div> <style> @keyframes scan { 0%, 100% { transform: translateY(-30px); } 50% { transform: translateY(30px); } } .scanner-laser { animation: scan 2s ease-in-out infinite; } </style> </div> <!-- Dashboard / Results Area --> <div class="bg-white rounded-t-3xl shadow-[0_-10px_20px_-10px_rgba(0,0,0,0.1)] p-6 z-30 flex flex-col gap-5 -mt-4"> <!-- Result Card --> <div class="bg-indigo-50/50 rounded-2xl p-4 border border-indigo-100 min-h-[100px] flex flex-col justify-center relative group"> <p class="text-xs text-indigo-400 uppercase font-bold tracking-wider mb-2">Scanned Result</p> <p id="result-text" class="text-gray-800 font-mono text-base break-all select-all">No barcode scanned yet.</p> <p id="result-format" class="text-xs text-gray-400 mt-2 hidden">Format: <span></span></p> </div> <!-- Action Buttons --> <div class="flex gap-3 mt-1"> <button id="copy-btn" disabled class="flex-1 bg-gray-100 hover:bg-gray-200 text-gray-500 py-4 rounded-xl font-medium flex flex-col items-center justify-center gap-1 transition-all disabled:opacity-40 disabled:cursor-not-allowed"> <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> <span class="text-xs">Copy</span> </button> <button id="action-btn" class="flex-[3] bg-indigo-600 hover:bg-indigo-700 text-white py-4 rounded-xl font-bold shadow-lg shadow-indigo-200 transition-all flex items-center justify-center gap-2"> <svg id="action-icon" xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg> <span id="action-text">Start Scanning</span> </button> </div> </div> </div> <!-- Notification Toast --> <div id="toast" class="fixed top-5 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-6 py-3 rounded-full shadow-2xl z-50 transition-all duration-300 opacity-0 pointer-events-none flex items-center gap-2"> <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-green-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg> <span id="toast-msg">Copied to clipboard!</span> </div> <script> // App State let html5QrCode; let isScanning = false; let audioCtx; // DOM Elements const readerEl = document.getElementById('reader'); const placeholderEl = document.getElementById('camera-placeholder'); const overlayEl = document.getElementById('scan-overlay'); const actionBtn = document.getElementById('action-btn'); const actionText = document.getElementById('action-text'); const actionIcon = document.getElementById('action-icon'); const copyBtn = document.getElementById('copy-btn'); const resultText = document.getElementById('result-text'); const resultFormat = document.getElementById('result-format'); const statusIndicator = document.getElementById('status-indicator'); const toast = document.getElementById('toast'); // Initialize Library window.onload = () => { html5QrCode = new Html5Qrcode("reader"); actionBtn.addEventListener('click', () => { if (isScanning) { stopScanner(); } else { startScanner(); } }); copyBtn.addEventListener('click', copyResult); }; // Create a local audio beep without requiring external audio files function unlockAudio() { if (!audioCtx) { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } if (audioCtx.state === 'suspended') { audioCtx.resume(); } } function playBeep() { if (!audioCtx) return; const osc = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(800, audioCtx.currentTime); // Pitch gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime); // Volume gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1); osc.connect(gainNode); gainNode.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 0.1); } function startScanner() { unlockAudio(); // Important for iOS to allow sound // Configuration optimized for mobile const config = { fps: 15, qrbox: { width: 250, height: 250 }, aspectRatio: 1.0, formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE, Html5QrcodeSupportedFormats.UPC_A, Html5QrcodeSupportedFormats.UPC_E, Html5QrcodeSupportedFormats.EAN_13, Html5QrcodeSupportedFormats.EAN_8, Html5QrcodeSupportedFormats.CODE_39, Html5QrcodeSupportedFormats.CODE_128 ] }; // Attempt to launch rear camera html5QrCode.start( { facingMode: "environment" }, config, onScanSuccess, onScanFailure ) .then(() => { isScanning = true; updateUIState(); }) .catch((err) => { showToast("Camera permission denied or unavailable."); console.error("Error starting camera: ", err); }); } function stopScanner() { if (!isScanning) return; html5QrCode.stop().then(() => { isScanning = false; updateUIState(); }).catch((err) => { console.error("Error stopping camera: ", err); }); } function onScanSuccess(decodedText, decodedResult) { // Trigger local beep sound playBeep(); // Update UI with scanned result resultText.innerText = decodedText; if(decodedResult && decodedResult.result && decodedResult.result.format) { resultFormat.classList.remove('hidden'); resultFormat.querySelector('span').innerText = decodedResult.result.format.formatName; } resultText.classList.add('text-indigo-700', 'font-bold'); copyBtn.disabled = false; copyBtn.classList.remove('text-gray-500'); copyBtn.classList.add('text-indigo-600', 'hover:bg-indigo-100'); // Pause slightly to prevent rapid continuous scans of the same code html5QrCode.pause(); setTimeout(() => { if (isScanning) html5QrCode.resume(); }, 1500); } function onScanFailure(error) { // Background scanning errors - generally ignore these. } function updateUIState() { if (isScanning) { readerEl.classList.remove('hidden'); overlayEl.classList.remove('hidden'); placeholderEl.classList.add('hidden'); actionText.innerText = "Stop Scanning"; actionBtn.classList.remove('bg-indigo-600', 'hover:bg-indigo-700', 'shadow-indigo-200'); actionBtn.classList.add('bg-red-500', 'hover:bg-red-600', 'shadow-red-200'); // Change Icon to Stop actionIcon.innerHTML = '<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>'; statusIndicator.classList.replace('bg-red-400', 'bg-green-400'); } else { readerEl.classList.add('hidden'); overlayEl.classList.add('hidden'); placeholderEl.classList.remove('hidden'); actionText.innerText = "Start Scanning"; actionBtn.classList.remove('bg-red-500', 'hover:bg-red-600', 'shadow-red-200'); actionBtn.classList.add('bg-indigo-600', 'hover:bg-indigo-700', 'shadow-indigo-200'); // Change Icon to Play actionIcon.innerHTML = '<polygon points="5 3 19 12 5 21 5 3"></polygon>'; statusIndicator.classList.replace('bg-green-400', 'bg-red-400'); } } function copyResult() { const textToCopy = resultText.innerText; if (textToCopy === "No barcode scanned yet.") return; // Using execCommand as requested for maximum compatibility within frames/local files const textArea = document.createElement("textarea"); textArea.value = textToCopy; textArea.style.position = "fixed"; textArea.style.left = "-9999px"; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); showToast("Copied to clipboard!"); } catch (err) { showToast("Failed to copy text."); console.error("Copy error", err); } document.body.removeChild(textArea); } function showToast(message) { document.getElementById('toast-msg').innerText = message; toast.classList.remove('opacity-0', '-translate-y-4'); toast.classList.add('opacity-100', 'translate-y-0'); setTimeout(() => { toast.classList.remove('opacity-100', 'translate-y-0'); toast.classList.add('opacity-0', '-translate-y-4'); }, 2500); } </script> </body> </html>
No revisions found. Save the file to create a backup.
Delete
Update App