105 lines
2.7 KiB
JavaScript
105 lines
2.7 KiB
JavaScript
const video = document.getElementById('video');
|
|
const previewImg = document.getElementById('preview-img');
|
|
const canvas = document.getElementById('canvas');
|
|
const cameraStatus = document.getElementById('camera-status');
|
|
const studentNumberInput = document.getElementById('student-number');
|
|
const inputError = document.getElementById('input-error');
|
|
const captureBtn = document.getElementById('capture-btn');
|
|
const downloadLink = document.getElementById('download-link');
|
|
|
|
const OUTPUT_WIDTH = 145;
|
|
const OUTPUT_HEIGHT = 185;
|
|
const TARGET_RATIO = OUTPUT_WIDTH / OUTPUT_HEIGHT;
|
|
|
|
let captured = false;
|
|
|
|
async function startCamera() {
|
|
try {
|
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
|
|
video.srcObject = stream;
|
|
cameraStatus.classList.add('hidden');
|
|
captureBtn.disabled = false;
|
|
} catch (err) {
|
|
cameraStatus.textContent = 'Unable to access camera: ' + err.message;
|
|
}
|
|
}
|
|
|
|
function sanitizeFilename(name) {
|
|
return name.trim().replace(/[^a-z0-9_-]+/gi, '_');
|
|
}
|
|
|
|
function showLiveView() {
|
|
previewImg.classList.add('hidden');
|
|
video.classList.remove('hidden');
|
|
downloadLink.classList.add('hidden');
|
|
captureBtn.textContent = 'Capture Photo';
|
|
captured = false;
|
|
}
|
|
|
|
function showCapturedView() {
|
|
video.classList.add('hidden');
|
|
previewImg.classList.remove('hidden');
|
|
captureBtn.textContent = 'Retake Photo';
|
|
captured = true;
|
|
}
|
|
|
|
function capturePhoto() {
|
|
if (captured) {
|
|
showLiveView();
|
|
return;
|
|
}
|
|
|
|
const studentNumber = studentNumberInput.value.trim();
|
|
if (!studentNumber) {
|
|
inputError.classList.remove('hidden');
|
|
studentNumberInput.focus();
|
|
return;
|
|
}
|
|
inputError.classList.add('hidden');
|
|
|
|
const videoWidth = video.videoWidth;
|
|
const videoHeight = video.videoHeight;
|
|
const videoRatio = videoWidth / videoHeight;
|
|
|
|
let sx, sy, sw, sh;
|
|
if (videoRatio > TARGET_RATIO) {
|
|
sh = videoHeight;
|
|
sw = sh * TARGET_RATIO;
|
|
sx = (videoWidth - sw) / 2;
|
|
sy = 0;
|
|
} else {
|
|
sw = videoWidth;
|
|
sh = sw / TARGET_RATIO;
|
|
sx = 0;
|
|
sy = (videoHeight - sh) / 2;
|
|
}
|
|
|
|
canvas.width = OUTPUT_WIDTH;
|
|
canvas.height = OUTPUT_HEIGHT;
|
|
const ctx = canvas.getContext('2d');
|
|
ctx.drawImage(video, sx, sy, sw, sh, 0, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT);
|
|
|
|
const dataUrl = canvas.toDataURL('image/png');
|
|
const filename = `${sanitizeFilename(studentNumber)}.png`;
|
|
|
|
previewImg.src = dataUrl;
|
|
downloadLink.href = dataUrl;
|
|
downloadLink.download = filename;
|
|
downloadLink.classList.remove('hidden');
|
|
|
|
showCapturedView();
|
|
|
|
downloadLink.click();
|
|
}
|
|
|
|
captureBtn.addEventListener('click', capturePhoto);
|
|
|
|
studentNumberInput.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Enter') {
|
|
event.preventDefault();
|
|
capturePhoto();
|
|
}
|
|
});
|
|
|
|
startCamera();
|