diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/routes/(app)/scan/+page.svelte | 205 |
1 files changed, 31 insertions, 174 deletions
diff --git a/src/routes/(app)/scan/+page.svelte b/src/routes/(app)/scan/+page.svelte index 874fef96..b21a7458 100644 --- a/src/routes/(app)/scan/+page.svelte +++ b/src/routes/(app)/scan/+page.svelte @@ -1,204 +1,61 @@ <script> + import { tick } from "svelte"; + import Icon from "$comp/Icon.svelte"; + import QrScanner from "qr-scanner"; + import { onMount, onDestroy } from "svelte"; import { back } from "$lib/utils"; import { goto } from "$app/navigation"; - import { onMount, onDestroy } from "svelte"; import { BarcodeDetector } from "barcode-detector/ponyfill"; - import { camera } from "$lib/store"; - let resizing = $state(); + let scanner, + vid = $state(), + resizing; + let resize = () => { if (resizing) clearTimeout(resizing); resizing = setTimeout(initialize, 1000); }; - let videoEl = $state(); - let canvasEl = $state(); - let cameras = $state([]); - let isIOS = $state(); - let scanningStarted = $state(); - - let barcodeDetector; - let scanningFrameId; - let detectionResult = null; - - async function getCameraDevices() { - try { - const devices = await navigator.mediaDevices.enumerateDevices(); - cameras = devices.filter((device) => device.kind === "videoinput"); - - if (!$camera && cameras.length > 0) { - $camera = cameras[0].deviceId; - } - } catch (err) { - console.error("Error enumerating devices:", err); - } - } - - async function startCamera(deviceId) { - stopCameraStream(); - detectionResult = null; - try { - const constraints = deviceId - ? { video: { deviceId: { exact: deviceId } } } - : { video: { facingMode: "environment" } }; - const stream = await navigator.mediaDevices.getUserMedia(constraints); - videoEl.srcObject = stream; - await videoEl.play(); - } catch (err) { - console.error("Error accessing camera:", err); - } - } - - function stopCameraStream() { - if (videoEl && videoEl.srcObject) { - const stream = videoEl.srcObject; - if (stream.getTracks) { - stream.getTracks().forEach((track) => track.stop()); - } - } - if (scanningFrameId) { - cancelAnimationFrame(scanningFrameId); - } - } - - function handleDetection(barcode) { - detectionResult = barcode.rawValue; - stopCameraStream(); - goto(`/send/${encodeURIComponent(detectionResult)}`); - } - - function scanLoop() { - if (!videoEl.videoWidth || !videoEl.videoHeight) { - scanningFrameId = requestAnimationFrame(scanLoop); - return; - } - - const context = canvasEl.getContext("2d"); - if (!context) return; - - canvasEl.width = videoEl.videoWidth; - canvasEl.height = videoEl.videoHeight; - context.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height); - - barcodeDetector - .detect(canvasEl) - .then((barcodes) => { - if (barcodes.length > 0) { - handleDetection(barcodes[0]); - return; - } - }) - .catch((err) => { - console.error("Detection error:", err); - }); - - if (!detectionResult) { - scanningFrameId = requestAnimationFrame(scanLoop); + let initialize = async () => { + if (scanner) { + scanner.stop(); + await new Promise((r) => setTimeout(r, 1000)); } - } - - async function onCameraChange(event) { - $camera = event.target.value; - await startCamera($camera); - videoEl.addEventListener("loadeddata", () => { - canvasEl.width = videoEl.videoWidth; - canvasEl.height = videoEl.videoHeight; - scanLoop(); - }); - } - - async function restart() { - detectionResult = null; - await startCamera($camera); - videoEl.addEventListener("loadeddata", () => { - canvasEl.width = videoEl.videoWidth; - canvasEl.height = videoEl.videoHeight; - scanLoop(); - }); - } - - function startScanning() { - scanningStarted = true; - stopCameraStream(); - detectionResult = null; - const constraints = $camera - ? { video: { deviceId: { ideal: $camera } } } - : { video: { facingMode: "environment" } }; - navigator.mediaDevices - .getUserMedia(constraints) - .then((stream) => { - videoEl.srcObject = stream; - return videoEl.play(); - }) - .then(() => { - canvasEl.width = videoEl.videoWidth; - canvasEl.height = videoEl.videoHeight; - scanLoop(); - }) - .catch((err) => { - console.error("Error accessing camera:", err); - }); - } - let initialize = async () => { - isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); - barcodeDetector = new BarcodeDetector({ formats: ["qr_code"] }); - await getCameraDevices(); - if (!isIOS) startScanning(); + let options = { + highlightScanRegion: true, + highlightCodeOutline: true, + qrEngine: BarcodeDetector, + }; + let cb = ({ data }) => + scanner.stop() || goto(`/send/${encodeURIComponent(data)}`); + scanner = new QrScanner(vid, cb, options); + await tick(); + scanner.start(); }; onMount(initialize); - - onDestroy(() => { - stopCameraStream(); - }); + onDestroy(() => scanner?.stop()); </script> <svelte:window onresize={resize} /> + <div class="flex w-full mb-4 p-4"> - <div class="mx-auto rounded-3xl space-y-5"> + <div class="mx-auto rounded-3xl"> <video - bind:this={videoEl} + bind:this={vid} class="border-4 rounded-3xl border-black max-h-[calc(100vh*0.7)]" ></video> - <canvas bind:this={canvasEl} style="display: none;"></canvas> - <div class="flex justify-center"> - <button class="btn !w-auto" onclick={back}>Cancel</button> - </div> - - {#if cameras.length > 1} - <div> - <select - id="cameraSelect" - bind:value={$camera} - onchange={onCameraChange} - > - {#each cameras as camera} - <option value={camera.deviceId}> - {camera.label || `Camera ${camera.deviceId}`} - </option> - {/each} - </select> - </div> - {/if} - - {#if !scanningStarted} - <div class="flex justify-center"> - <button class="btn !w-auto" onclick={startScanning}> - Start Scanning - </button> - </div> - {/if} </div> </div> +<div class="flex justify-center"> + <button class="btn !w-auto" onclick={back}>Cancel</button> +</div> + <style> video { - width: 100%; - max-width: 600px; - border: 4px solid black; - border-radius: 1rem; - transform: scale(1); transition: transform 0.3s ease-out; + transform: scale(1); } </style> |
