| Cỡ chữ:   
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="style.css"> </head> <body> <div class="container"> <h1>Đọc Mã QR từ Hình ảnh</h1> <div class="input-section"> <input type="file" id="qrImageInput" accept="image/*"> <label for="qrImageInput" class="upload-button">Tải ảnh Mã QR lên</label> <p>Hoặc kéo và thả hình ảnh vào ô bên dưới</p> </div> <div class="canvas-container" id="canvasContainer"> <canvas id="qrCanvas"></canvas> <p class="placeholder-text">Kéo hoặc nhấp để tải ảnh lên</p> </div> <div class="result-section"> <h2>Nội dung Mã QR:</h2> <textarea id="qrContent" rows="5" placeholder="Nội dung mã QR sẽ hiển thị ở đây..." readonly></textarea> </div> </div> <script src="https://cdn.jsdelivr.net/npm/jsqr@1.1.0/dist/jsQR.min.js"></script> <script src="script.js"></script> </body> </html>
body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; background-color: #f4f7f6; margin: 0; padding: 20px; box-sizing: border-box; } .container { background-color: #ffffff; padding: 30px; border-radius: 10px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); text-align: center; width: 100%; max-width: 600px; } h1 { color: #333; margin-bottom: 25px; } .input-section { margin-bottom: 30px; } input[type="file"] { display: none; /* Ẩn input file mặc định */ } .upload-button { display: inline-block; background-color: #4CAF50; color: white; padding: 12px 25px; border-radius: 5px; cursor: pointer; font-size: 16px; transition: background-color 0.3s ease; margin-bottom: 10px; } .upload-button:hover { background-color: #45a049; } .input-section p { color: #777; font-size: 14px; } .canvas-container { border: 2px dashed #ccc; border-radius: 8px; width: 100%; min-height: 300px; /* Chiều cao tối thiểu cho phép kéo thả */ display: flex; justify-content: center; align-items: center; overflow: hidden; position: relative; background-color: #fafafa; margin-bottom: 30px; } .canvas-container.dragover { border-color: #007bff; background-color: #e6f7ff; } #qrCanvas { max-width: 100%; max-height: 100%; display: block; /* Loại bỏ khoảng trắng dưới canvas */ } .placeholder-text { position: absolute; color: #aaa; font-size: 18px; pointer-events: none; /* Đảm bảo các sự kiện chuột đi qua văn bản này đến canvas */ } .result-section h2 { color: #333; margin-bottom: 15px; } #qrContent { width: calc(100% - 20px); /* Đảm bảo padding không làm tràn */ padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; color: #555; resize: vertical; /* Cho phép thay đổi kích thước theo chiều dọc */ min-height: 100px; background-color: #f9f9f9; }
document.addEventListener('DOMContentLoaded', () => { const qrImageInput = document.getElementById('qrImageInput'); const qrCanvas = document.getElementById('qrCanvas'); const canvasContext = qrCanvas.getContext('2d'); const qrContent = document.getElementById('qrContent'); const canvasContainer = document.getElementById('canvasContainer'); const placeholderText = document.querySelector('.placeholder-text'); let image = new Image(); // Xử lý sự kiện khi chọn tệp bằng nút qrImageInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (file) { loadImage(file); } }); // Xử lý sự kiện kéo thả (Drag and Drop) canvasContainer.addEventListener('dragover', (event) => { event.preventDefault(); // Ngăn chặn hành vi mặc định canvasContainer.classList.add('dragover'); placeholderText.style.display = 'none'; // Ẩn placeholder khi kéo vào }); canvasContainer.addEventListener('dragleave', () => { canvasContainer.classList.remove('dragover'); if (!image.src) { // Chỉ hiện lại placeholder nếu không có ảnh placeholderText.style.display = 'block'; } }); canvasContainer.addEventListener('drop', (event) => { event.preventDefault(); // Ngăn chặn hành vi mặc định canvasContainer.classList.remove('dragover'); const file = event.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { loadImage(file); } else { alert('Vui lòng thả một tệp hình ảnh hợp lệ.'); } }); // Hàm tải và hiển thị hình ảnh function loadImage(file) { const reader = new FileReader(); reader.onload = (e) => { image.onload = () => { // Đảm bảo canvas có kích thước phù hợp với hình ảnh const aspectRatio = image.width / image.height; const maxWidth = canvasContainer.clientWidth; const maxHeight = canvasContainer.clientHeight; let drawWidth = image.width; let drawHeight = image.height; // Điều chỉnh kích thước hình ảnh để vừa với canvasContainer if (drawWidth > maxWidth || drawHeight > maxHeight) { if (drawWidth / maxWidth > drawHeight / maxHeight) { drawWidth = maxWidth; drawHeight = maxWidth / aspectRatio; } else { drawHeight = maxHeight; drawWidth = maxHeight * aspectRatio; } } // Cập nhật kích thước canvas qrCanvas.width = drawWidth; qrCanvas.height = drawHeight; canvasContext.clearRect(0, 0, qrCanvas.width, qrCanvas.height); // Xóa canvas cũ canvasContext.drawImage(image, 0, 0, qrCanvas.width, qrCanvas.height); // Vẽ hình ảnh decodeQRCode(); // Giải mã mã QR sau khi vẽ hình ảnh placeholderText.style.display = 'none'; // Ẩn placeholder khi có ảnh }; image.src = e.target.result; }; reader.readAsDataURL(file); } // Hàm giải mã mã QR function decodeQRCode() { if (!image.src) { qrContent.value = 'Chưa có hình ảnh để giải mã.'; return; } try { const imageData = canvasContext.getImageData(0, 0, qrCanvas.width, qrCanvas.height); const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: "dontInvert", // Có thể thử "dontInvert" hoặc "invertFirst" nếu gặp lỗi }); if (code) { qrContent.value = code.data; console.log('Nội dung mã QR:', code.data); } else { qrContent.value = 'Không tìm thấy mã QR trong hình ảnh.'; } } catch (error) { qrContent.value = 'Lỗi khi giải mã mã QR: ' + error.message; console.error('Lỗi giải mã:', error); } } // Xử lý khi canvas container được nhấp để chọn tệp canvasContainer.addEventListener('click', () => { qrImageInput.click(); }); // Ẩn placeholder ban đầu nếu canvas đã có ảnh (sẽ không xảy ra khi tải lần đầu) if (image.src) { placeholderText.style.display = 'none'; } });