├── .gitignore ├── .husky └── pre-commit ├── .prettierrc.json ├── .prettierignore ├── public ├── images │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── icon-192x192.png │ ├── icon-256x256.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ └── android-chrome-512x512.png ├── serviceWorker.js ├── manifest.json ├── js │ ├── app.js │ ├── minified │ │ └── prod.min.js │ └── scanner.js ├── css │ ├── output.css │ ├── style.css │ └── style.scss └── index.html ├── vercel.json ├── server.js ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | *.env -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run prod 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | public/js/minified/ 4 | public/css/output.css -------------------------------------------------------------------------------- /public/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/favicon-16x16.png -------------------------------------------------------------------------------- /public/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/favicon-32x32.png -------------------------------------------------------------------------------- /public/images/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/icon-192x192.png -------------------------------------------------------------------------------- /public/images/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/icon-256x256.png -------------------------------------------------------------------------------- /public/images/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/icon-384x384.png -------------------------------------------------------------------------------- /public/images/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/icon-512x512.png -------------------------------------------------------------------------------- /public/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/apple-touch-icon.png -------------------------------------------------------------------------------- /public/images/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/images/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murtuzaalisurti/qr/HEAD/public/images/android-chrome-512x512.png -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "builds": [ 3 | { 4 | "src": "server.js", 5 | "use": "@vercel/node" 6 | } 7 | ], 8 | "routes": [ 9 | { 10 | "src": "/(.*)", 11 | "dest": "server.js" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const path = require("path"); 4 | 5 | app.use(express.static("public")); 6 | 7 | app.get("/", (req, res) => { 8 | res.sendFile("index.html", { root: path.join(__dirname, "public") }); 9 | }); 10 | 11 | app.listen(process.env.PORT || 3000); 12 | 13 | module.exports = app; 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QRCodes 2 | 3 | A QR Code Generator and Reader! Generate as well as scan QR codes real quick! 4 | 5 | ## Tech Stack 6 | 7 |
 8 | HTML
 9 | CSS
10 | JavaScript
11 | 
12 | 13 | ## Tools used 14 | 15 | > To generate QR codes, we have used [qrcodejs](https://davidshimjs.github.io/qrcodejs/) javascript library. 16 | 17 | > To scan QR codes, we used the [html5-qrcode](https://github.com/mebjas/html5-qrcode) library which allows you to scan different types of codes! 18 | 19 | ### Note 20 | 21 | > The download option might not work in "In-App Browsers". It's better to use standalone browsers. 22 | -------------------------------------------------------------------------------- /public/serviceWorker.js: -------------------------------------------------------------------------------- 1 | const qrcodes = "qrcodes"; 2 | const assets = [ 3 | "/", 4 | "/index.html", 5 | "/css/output.css", 6 | "/js/app.js", 7 | "/js/scanner.js", 8 | ]; 9 | 10 | self.addEventListener("install", (installEvent) => { 11 | installEvent.waitUntil( 12 | caches.open(qrcodes).then((cache) => { 13 | cache.addAll(assets); 14 | }), 15 | ); 16 | }); 17 | 18 | self.addEventListener("fetch", (fetchEvent) => { 19 | fetchEvent.respondWith( 20 | // network request first instead of cache 21 | fetch(fetchEvent.request).catch(function () { 22 | return caches.match(fetchEvent.request); 23 | }), 24 | 25 | /*------------------------------------------*/ 26 | 27 | // cache first instead of network request 28 | 29 | /* caches.match(fetchEvent.request).then(res => { 30 | return res || fetch(fetchEvent.request) 31 | }) */ 32 | ); 33 | }); 34 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#344966", 3 | "background_color": "#344966", 4 | "display": "standalone", 5 | "orientation": "portrait", 6 | "scope": "/", 7 | "start_url": "index.html", 8 | "name": "QRCodes", 9 | "short_name": "QRCodes", 10 | "description": "Generate as well as Scan QR Codes!", 11 | "icons": [ 12 | { 13 | "src": "/images/icon-192x192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "/images/icon-256x256.png", 19 | "sizes": "256x256", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "/images/icon-384x384.png", 24 | "sizes": "384x384", 25 | "type": "image/png" 26 | }, 27 | { 28 | "src": "/images/icon-512x512.png", 29 | "sizes": "512x512", 30 | "type": "image/png" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qrcodes", 3 | "version": "1.0.0", 4 | "description": "A Simple QR Code Generator Web Application! Generate as well as download QR codes for anything real quick!", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js", 9 | "dev": "npx nodemon server.js", 10 | "sass": "sass --watch --no-source-map public/css/style.scss:public/css/style.css", 11 | "post": "postcss public/css/style.css -u autoprefixer cssnano -o public/css/output.css --no-map --watch", 12 | "minjs": "terser public/js/app.js public/js/scanner.js -c -m -o public/js/minified/prod.min.js", 13 | "prod": "terser public/js/app.js public/js/scanner.js -c -m -o public/js/minified/prod.min.js && prettier --write . && git add -A .", 14 | "prepare": "is-ci || husky" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/murtuzaalisurti/qr.git" 19 | }, 20 | "keywords": [], 21 | "author": "", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/murtuzaalisurti/qr/issues" 25 | }, 26 | "homepage": "https://github.com/murtuzaalisurti/qr#readme", 27 | "dependencies": { 28 | "@fortawesome/fontawesome-free": "^6.7.2", 29 | "express": "^4.21.2" 30 | }, 31 | "devDependencies": { 32 | "autoprefixer": "^10.4.21", 33 | "cssnano": "^7.0.6", 34 | "husky": "^9.1.7", 35 | "is-ci": "^4.1.0", 36 | "postcss": "^8.5.3", 37 | "postcss-cli": "^11.0.1", 38 | "prettier": "^3.5.3", 39 | "sass": "^1.86.0", 40 | "terser": "^5.39.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | let btn = document.querySelector(".button"); 2 | let qr_code_element = document.querySelector(".qr-code"); 3 | 4 | btn.addEventListener("click", () => { 5 | let user_input = document.querySelector("#input_text"); 6 | if (user_input.value != "") { 7 | if (qr_code_element.childElementCount == 0) { 8 | generate(user_input); 9 | } else { 10 | qr_code_element.innerHTML = ""; 11 | generate(user_input); 12 | } 13 | } else { 14 | document.querySelector(".error").style = ""; 15 | document.querySelector(".error").innerHTML = "Invalid Input!"; 16 | } 17 | }); 18 | 19 | function generate(user_input) { 20 | document.querySelector(".error").style = "display: none;"; 21 | 22 | var qrcode = new QRCode(qr_code_element, { 23 | text: `${user_input.value}`, 24 | width: 180, //128 25 | height: 180, 26 | colorDark: "#000000", 27 | colorLight: "#ffffff", 28 | correctLevel: QRCode.CorrectLevel.H, 29 | }); 30 | 31 | let download = document.createElement("button"); 32 | qr_code_element.appendChild(download); 33 | 34 | let download_link = document.createElement("a"); 35 | download_link.setAttribute("download", "qr_code.png"); 36 | download_link.innerHTML = `Download `; 37 | 38 | download.appendChild(download_link); 39 | 40 | let qr_code_img = document.querySelector(".qr-code img"); 41 | let qr_code_canvas = document.querySelector("canvas"); 42 | 43 | if (qr_code_img.getAttribute("src") == null) { 44 | setTimeout(() => { 45 | download_link.setAttribute("href", `${qr_code_canvas.toDataURL()}`); 46 | }, 300); 47 | } else { 48 | setTimeout(() => { 49 | download_link.setAttribute( 50 | "href", 51 | `${qr_code_img.getAttribute("src")}`, 52 | ); 53 | }, 300); 54 | } 55 | } 56 | 57 | generate({ 58 | value: "https://qr-codes.vercel.app", 59 | }); 60 | 61 | // genrate vs scan 62 | 63 | document.querySelectorAll(".select-section button").forEach((ele) => { 64 | ele.addEventListener("click", (e) => { 65 | document.querySelector(`.${e.target.classList[1]}-sec`).style = ""; 66 | e.target.nextElementSibling == null 67 | ? (document.querySelector( 68 | `.${e.target.previousElementSibling.classList[1]}-sec`, 69 | ).style = "display: none;") 70 | : (document.querySelector( 71 | `.${e.target.nextElementSibling.classList[1]}-sec`, 72 | ).style = "display: none;"); 73 | 74 | document.querySelector(`.${e.target.classList[1]}`).style = 75 | "border-color: #F0F4EF"; 76 | e.target.nextElementSibling == null 77 | ? (document.querySelector( 78 | `.${e.target.previousElementSibling.classList[1]}`, 79 | ).style = "") 80 | : (document.querySelector( 81 | `.${e.target.nextElementSibling.classList[1]}`, 82 | ).style = ""); 83 | }); 84 | }); 85 | 86 | if ("serviceWorker" in navigator) { 87 | window.addEventListener("load", function () { 88 | navigator.serviceWorker 89 | .register("/serviceWorker.js") 90 | .then((res) => console.log("service worker registered")) 91 | .catch((err) => console.log("service worker not registered", err)); 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /public/js/minified/prod.min.js: -------------------------------------------------------------------------------- 1 | let btn=document.querySelector(".button"),qr_code_element=document.querySelector(".qr-code");function generate(e){document.querySelector(".error").style="display: none;";new QRCode(qr_code_element,{text:`${e.value}`,width:180,height:180,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRCode.CorrectLevel.H});let t=document.createElement("button");qr_code_element.appendChild(t);let r=document.createElement("a");r.setAttribute("download","qr_code.png"),r.innerHTML='Download ',t.appendChild(r);let n=document.querySelector(".qr-code img"),o=document.querySelector("canvas");null==n.getAttribute("src")?setTimeout((()=>{r.setAttribute("href",`${o.toDataURL()}`)}),300):setTimeout((()=>{r.setAttribute("href",`${n.getAttribute("src")}`)}),300)}var cameraId;btn.addEventListener("click",(()=>{let e=document.querySelector("#input_text");""!=e.value?(0==qr_code_element.childElementCount||(qr_code_element.innerHTML=""),generate(e)):(document.querySelector(".error").style="",document.querySelector(".error").innerHTML="Invalid Input!")})),generate({value:"https://qr-codes.vercel.app"}),document.querySelectorAll(".select-section button").forEach((e=>{e.addEventListener("click",(e=>{document.querySelector(`.${e.target.classList[1]}-sec`).style="",null==e.target.nextElementSibling?document.querySelector(`.${e.target.previousElementSibling.classList[1]}-sec`).style="display: none;":document.querySelector(`.${e.target.nextElementSibling.classList[1]}-sec`).style="display: none;",document.querySelector(`.${e.target.classList[1]}`).style="border-color: #F0F4EF",null==e.target.nextElementSibling?document.querySelector(`.${e.target.previousElementSibling.classList[1]}`).style="":document.querySelector(`.${e.target.nextElementSibling.classList[1]}`).style=""}))})),"serviceWorker"in navigator&&window.addEventListener("load",(function(){navigator.serviceWorker.register("/serviceWorker.js").then((e=>console.log("service worker registered"))).catch((e=>console.log("service worker not registered",e)))}));let scanner=new Html5Qrcode("reader");function scan(){Html5Qrcode.getCameras().then((e=>{if(document.querySelector(".scan-btn").style="display: none;",document.querySelector(".scan-btn").innerHTML="Allow access to camera",document.querySelector("#camera").style="",document.querySelector(".scan").style="",e&&e.length){let t=document.querySelector("#camera");for(let r=0;r{""!==(cameraId=t.options[t.selectedIndex].value)?(document.querySelector("#camera").style="display: none;",scanner.start(cameraId,{fps:24,qrbox:{width:300,height:300}},((e,t)=>{document.querySelector(".res").innerText=`${e}`,document.querySelector(".res").style="",document.querySelector(".copy-btn").style="",console.log(`Code matched = ${t}`),document.querySelector(".res").scrollIntoView({behavior:"smooth"}),scanner.pause(!0)}),(e=>{})).then((()=>{console.log("camera started"),document.querySelector(".scan").disabled=!0,document.querySelector(".stop-btn").disabled=!1,document.querySelector(".stop-btn").style=""})).catch((e=>{console.error(e)}))):document.querySelector("#camera").focus()}))}})).catch((e=>{console.error(e),document.querySelector(".scan-btn").style="",document.querySelector(".scan-btn").disabled=!0,document.querySelector(".error-msg").style=""}))}function stopScan(){scanner.stop().then((e=>{console.log("Scan stopped"),document.querySelector(".res").style="display: none;",document.querySelector(".copy-btn").style="display: none;",document.querySelector(".scan").disabled=!1,document.querySelector(".stop-btn").disabled=!0,document.querySelector(".stop-btn").style="display: none",document.querySelector("#camera").style=""})).catch((e=>{console.log(e)}))}document.querySelector(".scan-btn").addEventListener("click",(()=>{scan()})),document.querySelector(".stop-btn").addEventListener("click",(()=>{stopScan()})),document.querySelector(".copy-btn").addEventListener("click",(e=>{navigator.clipboard?navigator.clipboard.writeText(e.target.previousElementSibling.innerText).then((()=>{e.target.innerHTML='Copied ',setTimeout((()=>{e.target.innerHTML="Copy"}),1500)})).catch((t=>{console.error(t),e.target.innerHTML='Copy failed ',setTimeout((()=>{e.target.innerHTML="Copy"}),1500)})):(e.target.innerHTML='Copy failed ',setTimeout((()=>{e.target.innerHTML="Copy"}),1500))})); -------------------------------------------------------------------------------- /public/css/output.css: -------------------------------------------------------------------------------- 1 | :root{font-size:62.5%}*{box-sizing:border-box;margin:0;padding:0;-moz-text-size-adjust:none;text-size-adjust:none;-webkit-text-size-adjust:none}button:hover{cursor:pointer}body{background-color:#344966;display:flex;flex-direction:column;height:100vh;max-height:-moz-fit-content;max-height:fit-content;position:relative}body::-webkit-scrollbar{width:.8rem}body::-webkit-scrollbar-thumb{background-color:#70819a;border-radius:1rem}body::-webkit-scrollbar-track{background-color:transparent;border-radius:1rem}@media screen and (max-width:50em){body{flex-direction:column;height:100%}}body .select-section{align-items:center;display:flex;justify-content:center;left:50%;position:absolute;top:20rem;transform:translateX(-50%);width:-moz-fit-content;width:fit-content}body .select-section .toggle-btn{background-color:#2b3d55;border:.2rem solid transparent;border-radius:.6rem;color:#f0f4ef;font-family:Poppins,sans-serif;margin:0 1rem;outline:none;padding:1rem 2rem}body .qr-code-generator-sec{align-items:center;display:flex;justify-content:center;padding:30rem 0 5rem;width:100%}@media screen and (max-width:50em){body .qr-code-generator-sec{flex-direction:column}}body .qr-code-reader-sec{align-items:center;display:flex;flex-direction:column;justify-content:center;padding:30rem 0 5rem;width:100%}body .qr-code-reader-sec .scan-btn{opacity:1}body .qr-code-reader-sec .scan-btn:disabled{cursor:not-allowed;opacity:.5}body .qr-code-reader-sec .error-msg{background-color:rgba(206,0,0,.34);border-radius:1rem;color:#f0f4ef;font-family:Poppins,sans-serif;font-size:1.5rem;margin:2rem;padding:2rem 3rem;text-align:center}body .qr-code-reader-sec .error-msg i{color:#ff9292;font-size:3.5rem}body .qr-code-reader-sec #camera{background-color:#bfcc94;border:none;border-radius:.5rem;color:#000;font-family:Poppins,sans-serif;font-size:1.6rem;margin:2rem;padding:1rem 2rem;transform:translateX(0)}@keyframes shiver{0%{transform:translateX(0)}25%{transform:translateX(3%)}75%{transform:translateX(-3%)}to{transform:translateX(0)}}body .qr-code-reader-sec #camera:focus{animation:shiver .3s ease-in-out}body .qr-code-reader-sec .controls{margin:2rem 0}body .qr-code-reader-sec .controls .ctrl-button{background-color:#2b3d55;border:.2rem solid transparent;border-radius:.6rem;color:#f0f4ef;font-family:Poppins,sans-serif;margin:0 1rem;opacity:1;outline:none;padding:1rem 2rem}body .qr-code-reader-sec .controls .ctrl-button:disabled{cursor:not-allowed;opacity:.5}body .qr-code-reader-sec .res{background-color:rgba(80,103,136,.63);border:.3rem solid rgba(139,160,191,.63);border-radius:1rem;color:#fff;font-family:Poppins,sans-serif;font-size:2rem;margin:2rem 0;max-width:-moz-fit-content;max-width:fit-content;overflow:auto;padding:2rem;text-align:center;white-space:nowrap;width:80%}body .qr-code-reader-sec .res::-webkit-scrollbar{height:.55rem}body .qr-code-reader-sec .res::-webkit-scrollbar-thumb{background-color:#70819a;border-radius:1rem}body .qr-code-reader-sec .res::-webkit-scrollbar-track{background-color:transparent;border-radius:1rem}body .qr-code-reader-sec .copy-btn{background-color:#2b3d55;border:.2rem solid transparent;border-radius:.6rem;color:#f0f4ef;font-family:Poppins,sans-serif;margin:0 0 2rem;opacity:1;outline:none;padding:1rem 2rem}body .heading{left:50%;position:absolute;top:5rem;transform:translateX(-50%)}body .sub-title,body .title{color:#f0f4ef;font-family:Poppins,sans-serif;font-size:4rem;text-align:center}body .sub-title{color:#f0f4ef;font-size:1.5rem;opacity:.5}body .user-input-section{align-items:center;background-color:hsla(0,0%,100%,.04);border-radius:1rem;display:flex;flex-direction:column;height:100%;justify-content:center;width:40%}@media screen and (max-width:50em){body .user-input-section{margin:2rem 0 0;padding:2.5rem;width:85%}}body .user-input-section .user-input{align-items:center;display:flex;flex-direction:column;justify-content:center;width:100%}body .user-input-section .user-input .error{background-color:#000;border:.2rem solid red;border-radius:.8rem;color:#fff;font-family:Poppins,sans-serif;font-size:2rem;padding:1rem}body .user-input-section .user-input label{font-family:Poppins,sans-serif;font-size:1.5rem;text-align:center}body .user-input-section .user-input input{background-color:#b4cded;border:none;border-radius:.5rem;color:#000;font-family:Poppins,sans-serif;margin:1rem 0 2rem;max-width:35rem;outline:none;padding:1.5rem 1rem;text-align:center;width:80%}body .user-input-section .user-input input::-moz-placeholder{color:#344966}body .user-input-section .user-input input::placeholder{color:#344966}body .button{background-color:#bfcc94;border:none;border-radius:.5rem;color:#000;font-family:Nunito,sans-serif;font-size:1.6rem;outline:none;padding:1.5rem 2.5rem}body .button i{margin-left:1rem}body .qr-code-container{align-items:center;display:flex;justify-content:center;width:30%}@media screen and (max-width:50em){body .qr-code-container{margin:8rem 0;width:100%}}body .qr-code-container .qr-code{background-color:hsla(0,0%,100%,.041);border-radius:1rem;display:flex;flex-direction:column;justify-content:space-between;padding:2rem;width:-moz-fit-content;width:fit-content}body .qr-code-container .qr-code button{background-color:#bfcc94;border:none;border-radius:1rem;color:#000;display:flex;justify-content:center;margin-top:2.5rem;outline:none;width:100%}body .qr-code-container .qr-code button a{color:#000;font-family:Poppins,sans-serif;font-size:1.5rem;height:100%;padding:1rem;text-decoration:none;width:100%}body .qr-code-container .qr-code button a i{margin-left:.5rem} -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 19 | 25 | 31 | QRCodes | QR Code Reader & Generator 32 | 33 | 34 | 35 | 42 | 46 | 52 | 56 | 57 | 58 |
59 |
QRcodes
60 |
Generate as well as Scan QRCode!
61 |
62 |
63 | 69 | 72 |
73 | 93 | 94 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /public/js/scanner.js: -------------------------------------------------------------------------------- 1 | var cameraId; 2 | let scanner = new Html5Qrcode("reader"); 3 | 4 | function scan() { 5 | // This method will trigger user permissions 6 | Html5Qrcode.getCameras() 7 | .then((devices) => { 8 | document.querySelector(".scan-btn").style = "display: none;"; 9 | document.querySelector(".scan-btn").innerHTML = 10 | "Allow access to camera"; 11 | 12 | document.querySelector("#camera").style = ""; 13 | document.querySelector(".scan").style = ""; 14 | /** 15 | * devices would be an array of objects of type: 16 | * { id: "id", label: "label" } 17 | */ 18 | if (devices && devices.length) { 19 | let select = document.querySelector("#camera"); 20 | 21 | for (let k = 0; k < devices.length; k++) { 22 | let option = document.createElement("option"); 23 | option.value = devices[k].id; 24 | option.innerHTML = devices[k].label; 25 | select.appendChild(option); 26 | } 27 | 28 | document 29 | .querySelector(".scan") 30 | .addEventListener("click", () => { 31 | cameraId = select.options[select.selectedIndex].value; 32 | // .. use this to start scanning. 33 | 34 | if (cameraId !== "") { 35 | document.querySelector("#camera").style = 36 | "display: none;"; 37 | 38 | scanner 39 | .start( 40 | cameraId, 41 | { 42 | fps: 24, 43 | qrbox: { 44 | width: 300, 45 | height: 300, 46 | }, 47 | }, 48 | (decodedText, decodedResult) => { 49 | document.querySelector( 50 | ".res", 51 | ).innerText = `${decodedText}`; 52 | document.querySelector(".res").style = 53 | ""; 54 | document.querySelector( 55 | ".copy-btn", 56 | ).style = ""; 57 | console.log( 58 | `Code matched = ${decodedResult}`, 59 | ); 60 | document 61 | .querySelector(".res") 62 | .scrollIntoView({ 63 | behavior: "smooth", 64 | }); 65 | scanner.pause(true); 66 | }, 67 | (errorMessage) => { 68 | // console.error(`Code scan error = ${errorMessage}`); 69 | }, 70 | ) 71 | .then(() => { 72 | console.log("camera started"); 73 | document.querySelector(".scan").disabled = 74 | true; 75 | document.querySelector( 76 | ".stop-btn", 77 | ).disabled = false; 78 | document.querySelector(".stop-btn").style = 79 | ""; 80 | }) 81 | .catch((err) => { 82 | console.error(err); 83 | }); 84 | } else { 85 | document.querySelector("#camera").focus(); 86 | } 87 | }); 88 | } 89 | }) 90 | .catch((err) => { 91 | // handle err 92 | console.error(err); 93 | document.querySelector(".scan-btn").style = ""; 94 | document.querySelector(".scan-btn").disabled = true; 95 | document.querySelector(".error-msg").style = ""; 96 | }); 97 | } 98 | 99 | document.querySelector(".scan-btn").addEventListener("click", () => { 100 | scan(); 101 | }); 102 | 103 | document.querySelector(".stop-btn").addEventListener("click", () => { 104 | stopScan(); 105 | }); 106 | 107 | document.querySelector(".copy-btn").addEventListener("click", (e) => { 108 | if (navigator.clipboard) { 109 | navigator.clipboard 110 | .writeText(e.target.previousElementSibling.innerText) 111 | .then(() => { 112 | e.target.innerHTML = `Copied `; 113 | setTimeout(() => { 114 | e.target.innerHTML = "Copy"; 115 | }, 1500); 116 | }) 117 | .catch((err) => { 118 | console.error(err); 119 | e.target.innerHTML = `Copy failed `; 120 | setTimeout(() => { 121 | e.target.innerHTML = "Copy"; 122 | }, 1500); 123 | }); 124 | } else { 125 | e.target.innerHTML = `Copy failed `; 126 | setTimeout(() => { 127 | e.target.innerHTML = "Copy"; 128 | }, 1500); 129 | } 130 | }); 131 | 132 | function stopScan() { 133 | scanner 134 | .stop() 135 | .then((ignore) => { 136 | // QR Code scanning is stopped. 137 | console.log("Scan stopped"); 138 | document.querySelector(".res").style = "display: none;"; 139 | document.querySelector(".copy-btn").style = "display: none;"; 140 | document.querySelector(".scan").disabled = false; 141 | document.querySelector(".stop-btn").disabled = true; 142 | document.querySelector(".stop-btn").style = "display: none"; 143 | document.querySelector("#camera").style = ""; 144 | }) 145 | .catch((err) => { 146 | // Stop failed, handle it. 147 | console.log(err); 148 | }); 149 | } 150 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-size: 62.5%; 3 | } 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | text-size-adjust: none; 10 | -webkit-text-size-adjust: none; 11 | } 12 | 13 | button:hover { 14 | cursor: pointer; 15 | } 16 | 17 | body { 18 | display: flex; 19 | flex-direction: column; 20 | background-color: #344966; 21 | height: 100vh; 22 | max-height: fit-content; 23 | position: relative; 24 | } 25 | body::-webkit-scrollbar { 26 | width: 0.8rem; 27 | } 28 | body::-webkit-scrollbar-thumb { 29 | background-color: rgb(112, 129, 154); 30 | border-radius: 1rem; 31 | } 32 | body::-webkit-scrollbar-track { 33 | background-color: transparent; 34 | border-radius: 1rem; 35 | } 36 | @media screen and (max-width: 50em) { 37 | body { 38 | flex-direction: column; 39 | height: 100%; 40 | } 41 | } 42 | body .select-section { 43 | position: absolute; 44 | top: 20rem; 45 | left: 50%; 46 | transform: translateX(-50%); 47 | width: fit-content; 48 | display: flex; 49 | justify-content: center; 50 | align-items: center; 51 | } 52 | body .select-section .toggle-btn { 53 | background-color: #2b3d55; 54 | outline: none; 55 | border: 0.2rem solid transparent; 56 | color: #f0f4ef; 57 | padding: 1rem 2rem; 58 | margin: 0 1rem; 59 | border-radius: 0.6rem; 60 | font-family: "Poppins", sans-serif; 61 | } 62 | body .qr-code-generator-sec { 63 | display: flex; 64 | align-items: center; 65 | justify-content: center; 66 | width: 100%; 67 | padding: 30rem 0 5rem 0; 68 | } 69 | @media screen and (max-width: 50em) { 70 | body .qr-code-generator-sec { 71 | flex-direction: column; 72 | } 73 | } 74 | body .qr-code-reader-sec { 75 | display: flex; 76 | flex-direction: column; 77 | align-items: center; 78 | justify-content: center; 79 | width: 100%; 80 | padding: 30rem 0 5rem 0; 81 | } 82 | body .qr-code-reader-sec .scan-btn { 83 | opacity: 1; 84 | } 85 | body .qr-code-reader-sec .scan-btn:disabled { 86 | opacity: 0.5; 87 | cursor: not-allowed; 88 | } 89 | body .qr-code-reader-sec .error-msg { 90 | padding: 2rem 3rem; 91 | font-size: 1.5rem; 92 | text-align: center; 93 | margin: 2rem; 94 | background-color: rgba(206, 0, 0, 0.34); 95 | color: #f0f4ef; 96 | border-radius: 1rem; 97 | font-family: "Poppins", sans-serif; 98 | } 99 | body .qr-code-reader-sec .error-msg i { 100 | font-size: 3.5rem; 101 | color: #ff9292; 102 | } 103 | body .qr-code-reader-sec #camera { 104 | padding: 1rem 2rem; 105 | background-color: #bfcc94; 106 | color: black; 107 | border: none; 108 | border-radius: 0.5rem; 109 | margin: 2rem; 110 | font-family: "Poppins", sans-serif; 111 | font-size: 1.6rem; 112 | transform: translateX(0); 113 | } 114 | @keyframes shiver { 115 | 0% { 116 | transform: translateX(0); 117 | } 118 | 25% { 119 | transform: translateX(3%); 120 | } 121 | 75% { 122 | transform: translateX(-3%); 123 | } 124 | 100% { 125 | transform: translateX(0); 126 | } 127 | } 128 | body .qr-code-reader-sec #camera:focus { 129 | animation: shiver 0.3s ease-in-out; 130 | } 131 | body .qr-code-reader-sec .controls { 132 | margin: 2rem 0; 133 | } 134 | body .qr-code-reader-sec .controls .ctrl-button { 135 | background-color: #2b3d55; 136 | outline: none; 137 | border: 0.2rem solid transparent; 138 | color: #f0f4ef; 139 | padding: 1rem 2rem; 140 | margin: 0 1rem; 141 | border-radius: 0.6rem; 142 | font-family: "Poppins", sans-serif; 143 | opacity: 1; 144 | } 145 | body .qr-code-reader-sec .controls .ctrl-button:disabled { 146 | opacity: 0.5; 147 | cursor: not-allowed; 148 | } 149 | body .qr-code-reader-sec .res { 150 | color: white; 151 | font-size: 2rem; 152 | background-color: rgba(80, 103, 136, 0.63); 153 | width: 80%; 154 | max-width: fit-content; 155 | padding: 2rem; 156 | font-family: "Poppins", sans-serif; 157 | border: 0.3rem solid rgba(139, 160, 191, 0.63); 158 | border-radius: 1rem; 159 | text-align: center; 160 | overflow: auto; 161 | white-space: nowrap; 162 | margin: 2rem 0; 163 | } 164 | body .qr-code-reader-sec .res::-webkit-scrollbar { 165 | height: 0.55rem; 166 | } 167 | body .qr-code-reader-sec .res::-webkit-scrollbar-thumb { 168 | background-color: rgb(112, 129, 154); 169 | border-radius: 1rem; 170 | } 171 | body .qr-code-reader-sec .res::-webkit-scrollbar-track { 172 | background-color: transparent; 173 | border-radius: 1rem; 174 | } 175 | body .qr-code-reader-sec .copy-btn { 176 | background-color: #2b3d55; 177 | outline: none; 178 | border: 0.2rem solid transparent; 179 | color: #f0f4ef; 180 | padding: 1rem 2rem; 181 | margin: 0 0 2rem 0; 182 | border-radius: 0.6rem; 183 | font-family: "Poppins", sans-serif; 184 | opacity: 1; 185 | } 186 | body .heading { 187 | position: absolute; 188 | top: 5rem; 189 | left: 50%; 190 | transform: translateX(-50%); 191 | } 192 | body .title, 193 | body .sub-title { 194 | font-size: 4rem; 195 | text-align: center; 196 | font-family: "Poppins", sans-serif; 197 | color: #f0f4ef; 198 | } 199 | body .sub-title { 200 | font-size: 1.5rem; 201 | color: #f0f4ef; 202 | opacity: 0.5; 203 | } 204 | body .user-input-section { 205 | align-items: center; 206 | display: flex; 207 | flex-direction: column; 208 | width: 40%; 209 | background-color: rgba(255, 255, 255, 0.04); 210 | height: 100%; 211 | justify-content: center; 212 | border-radius: 1rem; 213 | } 214 | @media screen and (max-width: 50em) { 215 | body .user-input-section { 216 | margin: 2rem 0 0 0; 217 | width: 85%; 218 | padding: 2.5rem; 219 | } 220 | } 221 | body .user-input-section .user-input { 222 | align-items: center; 223 | display: flex; 224 | flex-direction: column; 225 | width: 100%; 226 | justify-content: center; 227 | } 228 | body .user-input-section .user-input .error { 229 | font-family: "Poppins", sans-serif; 230 | font-size: 2rem; 231 | background-color: black; 232 | color: white; 233 | padding: 1rem; 234 | border: 0.2rem solid red; 235 | border-radius: 0.8rem; 236 | } 237 | body .user-input-section .user-input label { 238 | text-align: center; 239 | font-size: 1.5rem; 240 | font-family: "Poppins", sans-serif; 241 | } 242 | body .user-input-section .user-input input { 243 | width: 80%; 244 | max-width: 35rem; 245 | font-family: "Poppins", sans-serif; 246 | outline: none; 247 | border: none; 248 | border-radius: 0.5rem; 249 | background-color: #b4cded; 250 | text-align: center; 251 | padding: 1.5rem 1rem; 252 | margin: 1rem 0rem 2rem 0rem; 253 | color: black; 254 | } 255 | body .user-input-section .user-input input::placeholder { 256 | color: #344966; 257 | } 258 | body .button { 259 | outline: none; 260 | border: none; 261 | border-radius: 0.5rem; 262 | padding: 1.5rem 2.5rem; 263 | background-color: #bfcc94; 264 | color: black; 265 | font-family: "Nunito", sans-serif; 266 | font-size: 1.6rem; 267 | } 268 | body .button i { 269 | margin-left: 1rem; 270 | } 271 | body .qr-code-container { 272 | display: flex; 273 | justify-content: center; 274 | align-items: center; 275 | width: 30%; 276 | } 277 | @media screen and (max-width: 50em) { 278 | body .qr-code-container { 279 | width: 100%; 280 | margin: 8rem 0; 281 | } 282 | } 283 | body .qr-code-container .qr-code { 284 | display: flex; 285 | border-radius: 1rem; 286 | background-color: rgba(255, 255, 255, 0.041); 287 | width: fit-content; 288 | flex-direction: column; 289 | justify-content: space-between; 290 | padding: 2rem; 291 | } 292 | body .qr-code-container .qr-code button { 293 | display: flex; 294 | justify-content: center; 295 | background-color: #bfcc94; 296 | color: black; 297 | border: none; 298 | outline: none; 299 | width: 100%; 300 | margin-top: 2.5rem; 301 | border-radius: 1rem; 302 | } 303 | body .qr-code-container .qr-code button a { 304 | font-family: "Poppins", sans-serif; 305 | font-size: 1.5rem; 306 | width: 100%; 307 | height: 100%; 308 | text-decoration: none; 309 | color: black; 310 | padding: 1rem; 311 | } 312 | body .qr-code-container .qr-code button a i { 313 | margin-left: 0.5rem; 314 | } 315 | -------------------------------------------------------------------------------- /public/css/style.scss: -------------------------------------------------------------------------------- 1 | // author: Murtuzaali Surti 2 | 3 | $primary-color: #344966; 4 | $secondary-color: #f0f4ef; 5 | $tertiary-color: #b4cded; 6 | $qr-code: rgba(255, 255, 255, 0.041); 7 | $button-color: #bfcc94; 8 | $toggle-btn-color: #2b3d55; 9 | 10 | :root { 11 | font-size: 62.5%; 12 | } 13 | 14 | * { 15 | margin: 0; 16 | padding: 0; 17 | box-sizing: border-box; 18 | text-size-adjust: none; 19 | -webkit-text-size-adjust: none; 20 | } 21 | button:hover { 22 | cursor: pointer; 23 | } 24 | 25 | body { 26 | display: flex; 27 | flex-direction: column; 28 | background-color: $primary-color; 29 | height: 100vh; 30 | max-height: fit-content; 31 | position: relative; 32 | 33 | &::-webkit-scrollbar { 34 | width: 0.8rem; 35 | } 36 | 37 | &::-webkit-scrollbar-thumb { 38 | background-color: rgb(112 129 154); 39 | border-radius: 1rem; 40 | } 41 | 42 | &::-webkit-scrollbar-track { 43 | background-color: transparent; 44 | border-radius: 1rem; 45 | } 46 | 47 | @media screen and (max-width: 50em) { 48 | flex-direction: column; 49 | height: 100%; 50 | } 51 | 52 | .select-section { 53 | position: absolute; 54 | top: 20rem; 55 | left: 50%; 56 | transform: translateX(-50%); 57 | width: fit-content; 58 | display: flex; 59 | justify-content: center; 60 | align-items: center; 61 | 62 | .toggle-btn { 63 | background-color: $toggle-btn-color; 64 | outline: none; 65 | border: 0.2rem solid transparent; 66 | color: $secondary-color; 67 | padding: 1rem 2rem; 68 | margin: 0 1rem; 69 | border-radius: 0.6rem; 70 | font-family: "Poppins", sans-serif; 71 | } 72 | } 73 | 74 | .qr-code-generator-sec { 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | width: 100%; 79 | padding: 30rem 0 5rem 0; 80 | 81 | @media screen and (max-width: 50em) { 82 | flex-direction: column; 83 | } 84 | } 85 | 86 | .qr-code-reader-sec { 87 | display: flex; 88 | flex-direction: column; 89 | align-items: center; 90 | justify-content: center; 91 | width: 100%; 92 | padding: 30rem 0 5rem 0; 93 | 94 | .scan-btn { 95 | opacity: 1; 96 | 97 | &:disabled { 98 | opacity: 0.5; 99 | cursor: not-allowed; 100 | } 101 | } 102 | 103 | .error-msg { 104 | padding: 2rem 3rem; 105 | font-size: 1.5rem; 106 | text-align: center; 107 | margin: 2rem; 108 | background-color: rgb(206 0 0 / 34%); 109 | color: $secondary-color; 110 | border-radius: 1rem; 111 | font-family: "Poppins", sans-serif; 112 | 113 | i { 114 | font-size: 3.5rem; 115 | color: #ff9292; 116 | } 117 | } 118 | 119 | #camera { 120 | padding: 1rem 2rem; 121 | background-color: $button-color; 122 | color: black; 123 | border: none; 124 | border-radius: 0.5rem; 125 | margin: 2rem; 126 | font-family: "Poppins", sans-serif; 127 | font-size: 1.6rem; 128 | transform: translateX(0); 129 | 130 | @keyframes shiver { 131 | 0% { 132 | transform: translateX(0); 133 | } 134 | 25% { 135 | transform: translateX(3%); 136 | } 137 | 75% { 138 | transform: translateX(-3%); 139 | } 140 | 100% { 141 | transform: translateX(0); 142 | } 143 | } 144 | 145 | &:focus { 146 | animation: shiver 0.3s ease-in-out; 147 | } 148 | } 149 | 150 | .controls { 151 | margin: 2rem 0; 152 | 153 | .ctrl-button { 154 | background-color: $toggle-btn-color; 155 | outline: none; 156 | border: 0.2rem solid transparent; 157 | color: $secondary-color; 158 | padding: 1rem 2rem; 159 | margin: 0 1rem; 160 | border-radius: 0.6rem; 161 | font-family: "Poppins", sans-serif; 162 | opacity: 1; 163 | 164 | &:disabled { 165 | opacity: 0.5; 166 | cursor: not-allowed; 167 | } 168 | } 169 | } 170 | 171 | .res { 172 | color: white; 173 | font-size: 2rem; 174 | background-color: rgb(80 103 136 / 63%); 175 | width: 80%; 176 | max-width: fit-content; 177 | padding: 2rem; 178 | font-family: "Poppins", sans-serif; 179 | border: 0.3rem solid rgb(139 160 191 / 63%); 180 | border-radius: 1rem; 181 | text-align: center; 182 | overflow: auto; 183 | white-space: nowrap; 184 | margin: 2rem 0; 185 | 186 | &::-webkit-scrollbar { 187 | height: 0.55rem; 188 | } 189 | 190 | &::-webkit-scrollbar-thumb { 191 | background-color: rgb(112 129 154); 192 | border-radius: 1rem; 193 | } 194 | 195 | &::-webkit-scrollbar-track { 196 | background-color: transparent; 197 | border-radius: 1rem; 198 | } 199 | } 200 | 201 | .copy-btn { 202 | background-color: $toggle-btn-color; 203 | outline: none; 204 | border: 0.2rem solid transparent; 205 | color: $secondary-color; 206 | padding: 1rem 2rem; 207 | margin: 0 0 2rem 0; 208 | border-radius: 0.6rem; 209 | font-family: "Poppins", sans-serif; 210 | opacity: 1; 211 | } 212 | } 213 | 214 | .heading { 215 | position: absolute; 216 | top: 5rem; 217 | left: 50%; 218 | transform: translateX(-50%); 219 | } 220 | .title, 221 | .sub-title { 222 | font-size: 4rem; 223 | text-align: center; 224 | font-family: "Poppins", sans-serif; 225 | color: $secondary-color; 226 | } 227 | .sub-title { 228 | font-size: 1.5rem; 229 | color: $secondary-color; 230 | opacity: 0.5; 231 | } 232 | 233 | .user-input-section { 234 | align-items: center; 235 | display: flex; 236 | flex-direction: column; 237 | width: 40%; 238 | background-color: rgb(255 255 255 / 4%); 239 | height: 100%; 240 | justify-content: center; 241 | border-radius: 1rem; 242 | 243 | @media screen and (max-width: 50em) { 244 | margin: 2rem 0 0 0; 245 | width: 85%; 246 | padding: 2.5rem; 247 | } 248 | 249 | .user-input { 250 | align-items: center; 251 | display: flex; 252 | flex-direction: column; 253 | width: 100%; 254 | justify-content: center; 255 | 256 | .error { 257 | font-family: "Poppins", sans-serif; 258 | font-size: 2rem; 259 | background-color: black; 260 | color: white; 261 | padding: 1rem; 262 | border: 0.2rem solid red; 263 | border-radius: 0.8rem; 264 | } 265 | 266 | label { 267 | text-align: center; 268 | font-size: 1.5rem; 269 | font-family: "Poppins", sans-serif; 270 | } 271 | 272 | input { 273 | width: 80%; 274 | max-width: 35rem; 275 | font-family: "Poppins", sans-serif; 276 | outline: none; 277 | border: none; 278 | border-radius: 0.5rem; 279 | background-color: $tertiary-color; 280 | text-align: center; 281 | padding: 1.5rem 1rem; 282 | margin: 1rem 0rem 2rem 0rem; 283 | color: black; 284 | 285 | &::placeholder { 286 | color: $primary-color; 287 | } 288 | } 289 | } 290 | } 291 | 292 | .button { 293 | outline: none; 294 | border: none; 295 | border-radius: 0.5rem; 296 | padding: 1.5rem 2.5rem; 297 | background-color: $button-color; 298 | color: black; 299 | font-family: "Nunito", sans-serif; 300 | font-size: 1.6rem; 301 | 302 | i { 303 | margin-left: 1rem; 304 | } 305 | } 306 | 307 | .qr-code-container { 308 | display: flex; 309 | justify-content: center; 310 | align-items: center; 311 | width: 30%; 312 | 313 | @media screen and (max-width: 50em) { 314 | width: 100%; 315 | margin: 8rem 0; 316 | } 317 | 318 | .qr-code { 319 | display: flex; 320 | border-radius: 1rem; 321 | background-color: $qr-code; 322 | width: fit-content; 323 | flex-direction: column; 324 | justify-content: space-between; 325 | padding: 2rem; 326 | 327 | button { 328 | display: flex; 329 | justify-content: center; 330 | background-color: $button-color; 331 | color: black; 332 | border: none; 333 | outline: none; 334 | width: 100%; 335 | margin-top: 2.5rem; 336 | border-radius: 1rem; 337 | 338 | a { 339 | font-family: "Poppins", sans-serif; 340 | font-size: 1.5rem; 341 | width: 100%; 342 | height: 100%; 343 | text-decoration: none; 344 | color: black; 345 | padding: 1rem; 346 | 347 | i { 348 | margin-left: 0.5rem; 349 | } 350 | } 351 | } 352 | } 353 | } 354 | } 355 | --------------------------------------------------------------------------------