| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Blitzboom QR Code Generator</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <style> |
| .dark-mode { |
| background-color: #1a202c; |
| color: #f7fafc; |
| } |
| .dark-mode .card { |
| background-color: #2d3748; |
| border-color: #4a5568; |
| } |
| .dark-mode input:not([type="color"]), .dark-mode button:not([type="color"]) { |
| background-color: #4a5568; |
| border-color: #718096; |
| color: #f7fafc; |
| } |
| .dark-mode button:hover:not([type="color"]) { |
| background-color: #5a67d8; |
| } |
| .dark-mode .footer { |
| color: #a0aec0; |
| } |
| #qr-canvas, #qr-composite { |
| width: 100%; |
| height: 100%; |
| object-fit: contain; |
| background-color: white; |
| } |
| .card { |
| padding-bottom: 2rem; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 min-h-screen flex flex-col transition-colors duration-300"> |
| <div class="container mx-auto px-4 py-8 flex-grow flex flex-col"> |
| |
| <header class="text-center mb-8"> |
| <h1 class="text-3xl md:text-4xl font-bold text-indigo-600 mb-2">Blitzboom QR Code Generator</h1> |
| <p class="text-gray-600 dark:text-gray-300">Create QR codes instantly for any text or URL</p> |
| </header> |
|
|
| |
| <div class="card bg-white rounded-lg shadow-lg p-6 max-w-2xl mx-auto w-full dark:bg-gray-800 dark:border-gray-700 mb-8"> |
| |
| <div class="mb-8"> |
| <label class="block text-gray-700 dark:text-gray-300 mb-2 font-medium">QR Code Design</label> |
| <div class="grid grid-cols-2 gap-4 mb-4"> |
| <div> |
| <label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Foreground Color</label> |
| <input type="color" id="fg-color" value="#000000" class="w-full h-10"> |
| </div> |
| <div> |
| <label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Background Color</label> |
| <input type="color" id="bg-color" value="#ffffff" class="w-full h-10"> |
| </div> |
| </div> |
| <div class="mt-4"> |
| <label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Background Image (optional)</label> |
| <input type="file" id="bg-image" accept="image/*" class="w-full text-sm text-gray-500 |
| file:mr-4 file:py-2 file:px-4 |
| file:rounded-md file:border-0 |
| file:text-sm file:font-semibold |
| file:bg-indigo-50 file:text-indigo-700 |
| hover:file:bg-indigo-100 |
| dark:file:bg-gray-600 dark:file:text-gray-200 |
| dark:hover:file:bg-gray-500"> |
| </div> |
| </div> |
|
|
| <div class="mb-6"> |
| <label for="qr-input" class="block text-gray-700 dark:text-gray-300 mb-2 font-medium">Enter text or URL</label> |
| <div class="flex space-x-2"> |
| <input type="text" id="qr-input" placeholder="https://example.com" |
| class="flex-grow px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-700 dark:border-gray-600"> |
| <button id="copy-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition dark:bg-gray-600 dark:hover:bg-gray-500"> |
| <i data-feather="copy"></i> |
| </button> |
| </div> |
| <p id="error-message" class="text-red-500 text-sm mt-1 hidden">Please enter some text or URL</p> |
| </div> |
|
|
| |
| <button id="generate-btn" class="w-full bg-indigo-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-indigo-700 transition mt-4 mb-8 flex items-center justify-center"> |
| <i data-feather="zap" class="mr-2"></i> Generate QR Code |
| </button> |
|
|
| |
| <div id="qr-result" class="hidden mt-6"> |
| <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 mb-4"> |
| <h2 class="text-xl font-semibold mb-4 text-center dark:text-gray-300">Your QR Code</h2> |
| <div class="flex justify-center mb-4"> |
| <div class="relative" style="width: 200px; height: 200px;"> |
| <canvas id="qr-canvas" class="border border-gray-200 dark:border-gray-600 rounded-lg w-full h-full"></canvas> |
| <canvas id="qr-composite" class="hidden absolute top-0 left-0 border border-gray-200 dark:border-gray-600 rounded-lg w-full h-full"></canvas> |
| </div> |
| </div> |
| <div class="flex space-x-3 justify-center pt-4"> |
| <button id="download-btn" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center"> |
| <i data-feather="download" class="mr-2"></i> Download |
| </button> |
| <button id="clear-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition dark:bg-gray-600 dark:hover:bg-gray-500 flex items-center"> |
| <i data-feather="trash-2" class="mr-2"></i> Clear |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <footer class="footer text-center py-4 text-gray-500 dark:text-gray-400"> |
| <p>Powered by Blitzboom Tools</p> |
| <button id="dark-mode-toggle" class="mt-2 text-sm text-indigo-600 dark:text-indigo-400 hover:underline"> |
| Toggle Dark Mode |
| </button> |
| </footer> |
|
|
| <script> |
| |
| feather.replace(); |
| |
| |
| const darkModeToggle = document.getElementById('dark-mode-toggle'); |
| darkModeToggle.addEventListener('click', () => { |
| document.body.classList.toggle('dark-mode'); |
| localStorage.setItem('darkMode', document.body.classList.contains('dark-mode')); |
| }); |
| |
| |
| if (localStorage.getItem('darkMode') === 'true') { |
| document.body.classList.add('dark-mode'); |
| } |
| |
| |
| const qrInput = document.getElementById('qr-input'); |
| const generateBtn = document.getElementById('generate-btn'); |
| const qrResult = document.getElementById('qr-result'); |
| const qrCanvas = document.getElementById('qr-canvas'); |
| const qrComposite = document.getElementById('qr-composite'); |
| const downloadBtn = document.getElementById('download-btn'); |
| const clearBtn = document.getElementById('clear-btn'); |
| const copyBtn = document.getElementById('copy-btn'); |
| const errorMessage = document.getElementById('error-message'); |
| const fgColor = document.getElementById('fg-color'); |
| const bgColor = document.getElementById('bg-color'); |
| const bgImage = document.getElementById('bg-image'); |
| |
| generateBtn.addEventListener('click', () => { |
| const inputText = qrInput.value.trim(); |
| |
| if (!inputText) { |
| errorMessage.classList.remove('hidden'); |
| qrResult.classList.add('hidden'); |
| return; |
| } |
| |
| errorMessage.classList.add('hidden'); |
| |
| |
| QRCode.toCanvas(qrCanvas, inputText, { |
| width: 200, |
| margin: 2, |
| color: { |
| dark: fgColor.value, |
| light: bgColor.value |
| } |
| }, (error) => { |
| if (error) { |
| console.error(error); |
| return; |
| } |
| |
| |
| if (bgImage.files && bgImage.files[0]) { |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| const img = new Image(); |
| img.onload = function() { |
| |
| qrComposite.width = qrCanvas.width; |
| qrComposite.height = qrCanvas.height; |
| const ctx = qrComposite.getContext('2d'); |
| |
| |
| ctx.drawImage(img, 0, 0, qrComposite.width, qrComposite.height); |
| |
| |
| ctx.globalAlpha = 0.7; |
| ctx.drawImage(qrCanvas, 0, 0); |
| |
| qrCanvas.classList.add('hidden'); |
| qrComposite.classList.remove('hidden'); |
| qrResult.classList.remove('hidden'); |
| }; |
| img.src = e.target.result; |
| }; |
| reader.readAsDataURL(bgImage.files[0]); |
| } else { |
| qrCanvas.classList.remove('hidden'); |
| qrComposite.classList.add('hidden'); |
| qrResult.classList.remove('hidden'); |
| } |
| }); |
| }); |
| |
| downloadBtn.addEventListener('click', () => { |
| const link = document.createElement('a'); |
| link.download = 'blitzboom-qr.png'; |
| |
| const activeCanvas = qrComposite.classList.contains('hidden') ? qrCanvas : qrComposite; |
| link.href = activeCanvas.toDataURL('image/png'); |
| link.click(); |
| }); |
| |
| clearBtn.addEventListener('click', () => { |
| qrInput.value = ''; |
| bgImage.value = ''; |
| qrResult.classList.add('hidden'); |
| qrCanvas.classList.remove('hidden'); |
| qrComposite.classList.add('hidden'); |
| }); |
| |
| copyBtn.addEventListener('click', () => { |
| const inputText = qrInput.value.trim(); |
| if (inputText) { |
| navigator.clipboard.writeText(inputText).then(() => { |
| const originalIcon = copyBtn.innerHTML; |
| copyBtn.innerHTML = '<i data-feather="check"></i>'; |
| feather.replace(); |
| setTimeout(() => { |
| copyBtn.innerHTML = originalIcon; |
| feather.replace(); |
| }, 2000); |
| }); |
| } |
| }); |
| |
| |
| qrInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| generateBtn.click(); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|