51 lines
2.7 KiB
JavaScript
51 lines
2.7 KiB
JavaScript
import qrcode from './vendor/qrcode.mjs'
|
||
|
||
export function drawQr(canvas, text) {
|
||
const qr = qrcode(0, 'M'); qr.addData(text); qr.make()
|
||
const count = qr.getModuleCount(), scale = 5, margin = 4
|
||
canvas.width = canvas.height = (count + margin * 2) * scale
|
||
const ctx = canvas.getContext('2d'); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||
ctx.fillStyle = '#000'
|
||
for (let y = 0; y < count; y++) for (let x = 0; x < count; x++)
|
||
if (qr.isDark(y, x)) ctx.fillRect((x + margin) * scale, (y + margin) * scale, scale, scale)
|
||
}
|
||
|
||
export class InviteScanner {
|
||
constructor(video, onCode) { this.video = video; this.onCode = onCode; this.epoch = 0; this.canvas = document.createElement('canvas') }
|
||
decode(source, width, height) {
|
||
if (!width || !height) return null
|
||
const scale = Math.min(1, 1280 / Math.max(width, height))
|
||
this.canvas.width = Math.round(width * scale); this.canvas.height = Math.round(height * scale)
|
||
const ctx = this.canvas.getContext('2d', { willReadFrequently: true })
|
||
ctx.drawImage(source, 0, 0, this.canvas.width, this.canvas.height)
|
||
const pixels = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height)
|
||
return globalThis.jsQR(pixels.data, pixels.width, pixels.height)?.data
|
||
}
|
||
async image(file) {
|
||
this.stop(); const epoch = this.epoch
|
||
if (!file || file.size > 8 * 1024 * 1024) throw new Error('QR resmi en fazla 8 MB olabilir.')
|
||
const bitmap = await createImageBitmap(file)
|
||
let result
|
||
try { result = this.decode(bitmap, bitmap.width, bitmap.height) } finally { bitmap.close() }
|
||
if (epoch !== this.epoch) return
|
||
if (!result) throw new Error('Resimde QR okunamadı; daha net bir görüntü deneyin.')
|
||
await this.onCode(result)
|
||
}
|
||
async start() {
|
||
this.stop(); const epoch = this.epoch
|
||
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false })
|
||
if (epoch !== this.epoch) { stream.getTracks().forEach(t => t.stop()); return }
|
||
this.stream = stream; this.video.srcObject = stream; this.video.hidden = false
|
||
try {
|
||
await this.video.play()
|
||
if (epoch !== this.epoch) return
|
||
this.timer = setInterval(() => {
|
||
const result = this.decode(this.video, this.video.videoWidth, this.video.videoHeight)
|
||
if (result) { this.stop(); void this.onCode(result).catch(error => this.onError?.(error)) }
|
||
}, 250)
|
||
this.limit = setTimeout(() => this.stop(), 60000)
|
||
} catch (error) { this.stop(); throw error }
|
||
}
|
||
stop() { this.epoch++; clearInterval(this.timer); clearTimeout(this.limit); this.stream?.getTracks().forEach(t => t.stop()); this.stream = null; this.video.srcObject = null; this.video.hidden = true }
|
||
}
|