├── .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 |
95 |
96 |
97 |
98 |
Permission
99 | denied!
100 | Please allow access to camera and reload the page!
101 |
102 |
105 |
106 |
109 |
112 |
113 |
114 |
115 |
116 |
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 |
--------------------------------------------------------------------------------