├── README.md ├── indexedDBScript.js ├── index.html ├── style.css └── script.js /README.md: -------------------------------------------------------------------------------- 1 |

Browser Camera App

2 | 3 |

Simple Camera App written in HTML, CSS, and JavaScript which can be used to record and capture images.

4 | 5 |

Used Technologies

6 | 11 | 12 | #### Steps to Use: 13 | 14 | --- 15 | 16 | - Download or clone the repository 17 | 18 | ``` 19 | git clone https://github.com/Ayushparikh-code/Web-dev-mini-projects.git 20 | ``` 21 | 22 | - Go to the 'Browser Camera' directory 23 | - Run the index.html file 24 | - Start using the camera!! -------------------------------------------------------------------------------- /indexedDBScript.js: -------------------------------------------------------------------------------- 1 | let request = indexedDB.open("camera", 1); 2 | let db; 3 | 4 | request.onsuccess = function () { 5 | // if exist then will get db from here 6 | db = request.result; 7 | } 8 | request.onerror = function (err) { 9 | console.log(err) 10 | } 11 | request.onupgradeneeded = function () { 12 | // 1st time creation 13 | db = request.result; 14 | db.createObjectStore("images", { keyPath: "mid" }); 15 | db.createObjectStore("videos", { keyPath: "mid" }); 16 | } 17 | 18 | /* function addMediaToDB(data, table) { 19 | if (db) { 20 | // you need to get transaction 21 | let tx = db.transaction(table, "readwrite"); 22 | // get table refer 23 | let store = tx.objectStore(table); 24 | // add 25 | store.add({ mid: Date.now(), media: data }); 26 | 27 | } else { 28 | alert("db is loading") 29 | } 30 | } */ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Camera 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 |
00:00:00
30 | 31 |
32 | 33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | display: flex; 8 | flex-direction: row; 9 | background-color: black; 10 | } 11 | 12 | .video-container { 13 | height: 100vh; 14 | width: 100vw; 15 | margin: 0 auto; 16 | overflow: hidden; 17 | } 18 | 19 | video { 20 | height: 100%; 21 | width: 100%; 22 | transform: scale(-1, 1); 23 | } 24 | 25 | .filter-container { 26 | position: fixed; 27 | z-index: 2; 28 | justify-self: flex-start; 29 | align-self: center; 30 | left: 5vw; 31 | display: flex; 32 | flex-direction: column; 33 | background-color: white; 34 | } 35 | 36 | .filter { 37 | width: 70px; 38 | height: 50px; 39 | /* Lorem Picsum -> Sample Images */ 40 | background-image: url("https://picsum.photos/id/1015/70/50"); 41 | background-blend-mode: luminosity; 42 | margin: 1px; 43 | border: black 1px solid; 44 | } 45 | 46 | .filter-1 { 47 | background-color: #cc3838ad; 48 | } 49 | .filter-2 { 50 | background-color: #0fbbbbad; 51 | } 52 | .filter-3 { 53 | background-color: #8ddb18ad; 54 | } 55 | .filter-4 { 56 | background-color: #ee82eead; 57 | } 58 | .filter-5 { 59 | background-color: #b8870bad; 60 | } 61 | .filter-6 { 62 | background-color: #ffffff00; 63 | } 64 | 65 | .ui-filter { 66 | position: fixed; 67 | height: 100vh; 68 | width: 100vw; 69 | z-index: 2; 70 | opacity: 25%; 71 | } 72 | 73 | .timing-container { 74 | position: fixed; 75 | z-index: 2; 76 | justify-self: flex-start; 77 | align-self: flex-end; 78 | left: 5vw; 79 | top: 77vh; 80 | display: flex; 81 | flex-direction: column; 82 | background-color: #00000000; 83 | } 84 | 85 | .timing { 86 | font-size: 1.2rem; 87 | font-weight: 300; 88 | font-family: 'Courier New', Courier, monospace; 89 | color: white; 90 | display: none; 91 | } 92 | 93 | .timing-active { 94 | display: block; 95 | } 96 | 97 | .zoom-container { 98 | position: fixed; 99 | z-index: 2; 100 | justify-self: flex-end; 101 | top: 15vh; 102 | right: 7vw; 103 | display: flex; 104 | flex-direction: column; 105 | border-radius: 5%; 106 | background-color: rgba(61, 54, 54, 0.774); 107 | } 108 | 109 | .fas { 110 | padding: 7px; 111 | color: white; 112 | } 113 | 114 | .button-container { 115 | position: fixed; 116 | z-index: 2; 117 | justify-self: flex-end; 118 | display: flex; 119 | flex-direction: column; 120 | align-self: center; 121 | right: 5vw; 122 | } 123 | 124 | .record, .click { 125 | height: 65px; 126 | width: 65px; 127 | border-radius: 50%; 128 | background-color: #00000000; 129 | border: white solid 5px; 130 | margin: 10px 5px; 131 | } 132 | 133 | #record-video, #click-image { 134 | height: 45px; 135 | width: 45px; 136 | margin: 5px; 137 | border-radius: 50%; 138 | border: #00000000 solid 1px; 139 | font-size: 0.65rem; 140 | } 141 | 142 | #record-video { 143 | background-color: red; 144 | } 145 | 146 | #click-image { 147 | background-color: rgb(216, 216, 216); 148 | } 149 | 150 | .gallery-container { 151 | position: fixed; 152 | z-index: 2; 153 | justify-self: flex-end; 154 | display: flex; 155 | flex-direction: column; 156 | right: 5vw; 157 | bottom: 10vh; 158 | } 159 | 160 | .gallery { 161 | height: 55px; 162 | width: 55px; 163 | background-color: rgba(68, 68, 68, 0.493); 164 | border-radius: 50%; 165 | display: flex; 166 | align-items: center; 167 | justify-content: center; 168 | font-size: 1.45rem; 169 | color: white; 170 | } 171 | 172 | @keyframes buttonAnimation { 173 | 0% { 174 | transform: scale(1); 175 | } 176 | 50% { 177 | transform: scale(0.8); 178 | } 179 | 100% { 180 | transform: scale(1); 181 | } 182 | } -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | let video = document.querySelector("video"); 2 | 3 | let constraints = { 4 | video: true, 5 | audio: true 6 | }; 7 | 8 | let mediaRecorder; 9 | let buffer = []; 10 | 11 | let recordState = false; 12 | 13 | let videoRecorder = document.querySelector("#record-video"); 14 | let captureButton = document.querySelector("#click-image"); 15 | 16 | let timingELem = document.querySelector(".timing"); 17 | let clearObj; 18 | 19 | let allFilters = document.querySelectorAll(".filter"); 20 | let uiFilter = document.querySelector(".ui-filter"); 21 | 22 | let zoomIn = document.querySelector(".fa-plus"); 23 | let zoomOut = document.querySelector(".fa-minus"); 24 | 25 | let scale = 1.0; 26 | 27 | // Video recorder listeners 28 | navigator.mediaDevices 29 | .getUserMedia(constraints) 30 | .then(function (mediaStream) { 31 | video.srcObject = mediaStream; 32 | 33 | mediaRecorder = new MediaRecorder(mediaStream); 34 | mediaRecorder.addEventListener('dataavailable', function (e) { 35 | buffer.push(e.data); 36 | }); 37 | mediaRecorder.addEventListener('stop', function () { 38 | let blob = new Blob(buffer, { type: "video/mp4" }); 39 | const url = window.URL.createObjectURL(blob); 40 | let a = document.createElement("a"); 41 | a.download = `VID_${getTimeStamp()}.mp4`; 42 | a.href = url; 43 | a.click(); 44 | a.remove(); 45 | addMediaToDB(url, "videos"); 46 | buffer = []; 47 | }); 48 | }).catch(function (err) { 49 | console.log(err); 50 | }); 51 | 52 | // Recording Video 53 | videoRecorder.addEventListener('click', function () { 54 | if (!mediaRecorder) { 55 | alert("Permissions not given"); 56 | return; 57 | } 58 | if (!recordState) { 59 | mediaRecorder.start(); 60 | videoRecorder.style.animation = "buttonAnimation 0.75s infinite"; 61 | recordState = true; 62 | startClock(); 63 | } else { 64 | mediaRecorder.stop(); 65 | videoRecorder.style.animation = "none"; 66 | recordState = false; 67 | stopClock(); 68 | } 69 | }); 70 | 71 | // Capturing Image 72 | captureButton.addEventListener("click", function () { 73 | if (!mediaRecorder) { 74 | alert("Permissions not given"); 75 | return; 76 | } 77 | // Create a canvas element equal to video frame 78 | let canvas = document.createElement("canvas"); 79 | canvas.width = video.videoWidth; 80 | canvas.height = video.videoHeight; 81 | let tool = canvas.getContext("2d"); 82 | // Zoom from center 83 | tool.scale(scale, scale); 84 | let x = (tool.canvas.width / scale - video.videoWidth) / 2; 85 | let y = (tool.canvas.height / scale - video.videoHeight) / 2; 86 | // Draw a frame on that canvas 87 | tool.drawImage(video, x, y); 88 | // translucent 89 | tool.globalAlpha = 0.25; 90 | tool.fillStyle = getFilter(document.querySelector(".ui-filter").classList[1]); 91 | // things are drawn on above layer 92 | tool.fillRect(0, 0, canvas.width, canvas.height); 93 | // toDataUrl 94 | let link = canvas.toDataURL(); 95 | // Download 96 | let anchor = document.createElement("a"); 97 | anchor.href = link; 98 | anchor.download = `IMG_${getTimeStamp()}.png`; 99 | anchor.click(); 100 | anchor.remove(); 101 | canvas.remove(); 102 | addMediaToDB(link, "images"); 103 | captureButton.style.animation = "buttonAnimation 0.5s 1"; 104 | setTimeout(() => { 105 | captureButton.style.animation = "none"; 106 | }, 1000); 107 | }); 108 | 109 | // Get current time stamp for file name 110 | function getTimeStamp() { 111 | return new Date().getTime(); 112 | } 113 | 114 | // Filter helper 115 | function getFilter(filter) { 116 | switch (filter) { 117 | case "filter-1": return "#cc3838ad"; 118 | case "filter-2": return "#0fbbbbad"; 119 | case "filter-3": return "#8ddb18ad"; 120 | case "filter-4": return "#ee82eead"; 121 | case "filter-5": return "#b8870bad"; 122 | case "filter-6": return "#ffffff00"; 123 | } 124 | } 125 | 126 | // Video Recorder Timers 127 | function startClock() { 128 | timingELem.classList.add("timing-active"); 129 | let timeCount = 0; 130 | clearObj = setInterval(function () { 131 | let seconds = (timeCount % 60) < 10 ? `0${timeCount % 60}` : `${timeCount % 60}`; 132 | let minutes = (timeCount / 60) < 10 ? `0${Number.parseInt(timeCount / 60)}` : `${Number.parseInt(timeCount / 60)}`; 133 | let hours = (timeCount / 3600) < 10 ? `0${Number.parseInt(timeCount / 3600)}` : `${Number.parseInt(timeCount / 3600)}`; 134 | timingELem.innerText = `${hours}:${minutes}:${seconds}`; 135 | timeCount++; 136 | }, 1000); 137 | } 138 | function stopClock() { 139 | timingELem.classList.remove("timing-active"); 140 | timingELem.innerText = "00:00:00"; 141 | clearInterval(clearObj); 142 | } 143 | 144 | // Filter apply 145 | for (let i = 0; i < allFilters.length; i++) { 146 | allFilters[i].addEventListener("click", function () { 147 | uiFilter.classList.remove(uiFilter.classList[1]); 148 | uiFilter.classList.add(allFilters[i].classList[1]); 149 | }) 150 | } 151 | 152 | // Zoom apply 153 | zoomIn.addEventListener("click", function () { 154 | if (scale > 1.9) 155 | return; 156 | scale += 0.1; 157 | // -x for mirroring 158 | video.style.transform = `scale(-${scale}, ${scale})`; 159 | }) 160 | zoomOut.addEventListener("click", function () { 161 | if (scale < 0.5) 162 | return; 163 | scale -= 0.1; 164 | // -x for mirroring 165 | video.style.transform = `scale(-${scale}, ${scale})`; 166 | }) --------------------------------------------------------------------------------