quicksnap
Version:
QuickSnap is a lightweight, optimized Web Component for seamlessly integrating webcam access into JavaScript applications. It provides an efficient way to capture snapshots with a minimal yet powerful feature set, ensuring a smooth user experience.
2 lines (1 loc) • 7.84 kB
JavaScript
"use strict";function c(i,t,e){return typeof i=="number"&&!isNaN(i)&&i>0?i:typeof i=="string"&&!isNaN(Number(i))&&Number(i)>0?Number(i):(console.warn(`\u26A0\uFE0F Warning: Invalid ${t}. Expected a positive number, but received "${i}". Using default: ${e}.`),e)}function p(i,t,e){if(typeof i=="boolean")return i;if(typeof i=="string"){let n=i.toLowerCase();if(n==="true")return!0;if(n==="false")return!1}return console.warn(`\u26A0\uFE0F Warning: Invalid ${t}. Expected "true" or "false", but received "${i}". Using default: ${e}.`),e}function y(i,t,e,n){let r=Object.values(e);return r.includes(i)?i:(console.warn(`\u26A0\uFE0F Warning: Invalid ${t}. Expected one of [${r.join(", ")}], but received "${i}". Using default: "${n}".`),n)}function v(i,t,e){return typeof i=="string"&&i.trim().length>0?i.trim():(console.warn(`\u26A0\uFE0F Warning: Invalid ${t}. Expected a non-empty string, but received "${i}". Using default: "${e}".`),e)}var d=(i=>(i["image/png"]="image/png",i["image/jpeg"]="image/jpeg",i["image/webp"]="image/webp",i["image/bmp"]="image/bmp",i))(d||{});var b={width:{key:"width",default:640,validate:i=>c(i,"width",640)},height:{key:"height",default:480,validate:i=>c(i,"height",480)},autoStart:{key:"autostart",default:!0,validate:i=>p(i,"autostart",!0)},format:{key:"format",default:"image/png",validate:i=>y(i,"format",d,"image/png")},mediaDeviceId:{key:"mediaDeviceId",default:"",validate:i=>v(i,"mediaDeviceId","")}};var m=class{permission="prompt";currentStatus=null;async askAndGetStream(t,e,n=""){try{let r=await navigator.mediaDevices.getUserMedia({video:{width:{min:320,ideal:e,max:1920},height:{min:240,ideal:t,max:1080},deviceId:n},audio:!1});return this.permission="granted",r}catch(r){return console.error(`QuickSnap Error: ${r}`),this.permission="denied",null}}async watchPermission(t){try{this.currentStatus&&(this.currentStatus.onchange=null),this.currentStatus=await this.fetchPermissionStatus(),this.currentStatus.onchange=()=>{this.currentStatus&&(this.permission=this.currentStatus.state,t(this.permission))}}catch(e){console.error(`QuickSnap Error: ${e}`),this.permission="denied",t(this.permission)}}async fetchPermissionStatus(){return await navigator.permissions.query({name:"camera"})}};var{height:o,width:s,autoStart:u,format:l,mediaDeviceId:a}=b,h=class extends HTMLElement{containerElement;videoElement;overlayElement;webcam=new m;ready=!1;constructor(){super(),this.attachShadow({mode:"open"}),this.containerElement=document.createElement("div"),this.containerElement.style.position="relative",this.containerElement.style.display="inline-block",this.containerElement.style.maxWidth="100%",this.videoElement=document.createElement("video"),this.videoElement.style.height="100%",this.videoElement.style.maxWidth="100%",this.videoElement.setAttribute("playsinline","true"),this.overlayElement=document.createElement("div"),this.overlayElement.style.position="absolute",this.overlayElement.style.top="50%",this.overlayElement.style.left="50%",this.overlayElement.style.transform="translate(-50%, -50%)",this.overlayElement.style.color="#808080a3",this.overlayElement.style.fontSize="16px",this.overlayElement.style.fontWeight="bold",this.overlayElement.style.pointerEvents="none",this.containerElement.appendChild(this.videoElement),this.containerElement.appendChild(this.overlayElement),this.shadowRoot.append(this.containerElement)}static observedAttributes=[o.key,s.key,u.key,l.key,a.key];get width(){return this.hasAttribute(s.key)?s.validate(this.getAttribute(s.key)):s.default}set width(t){this.setAttribute(s.key,s.validate(t).toString()),this.start(!0)}get height(){return this.hasAttribute(o.key)?o.validate(this.getAttribute(o.key)):o.default}set height(t){this.setAttribute(o.key,s.validate(t).toString()),this.start(!0)}get format(){return this.hasAttribute(l.key)?l.validate(this.getAttribute(l.key)).toString():l.default.toString()}set format(t){this.setAttribute(l.key,l.validate(t).toString())}get autoStart(){return this.hasAttribute(u.key)?u.validate(this.getAttribute(u.key)):u.default}set autoStart(t){this.setAttribute(u.key,u.validate(t).toString())}get mediaDeviceId(){return this.hasAttribute(a.key)?a.validate(this.getAttribute(a.key)):a.default}set mediaDeviceId(t){this.setAttribute(a.key,a.validate(t).toString()),this.start(!0)}connectedCallback(){this.setStreamAndWatch()}attributeChangedCallback(t,e,n){e!==n&&[o.key,s.key,a.key].includes(t)&&this.start(!0)}dispatchCustomEvent(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e}))}onReady(){this.dispatchCustomEvent("ready",{message:"QuickSnap is ready for use."})}onCapture(t){this.dispatchCustomEvent("capture",{message:t?"Snapshot captured successfully.":"Failed to capture snapshot.",data:t})}onError(t){console.error(`QuickSnap Error: ${t}`),this.dispatchCustomEvent("quicksnapError",{message:t})}onPermissionStateUpdate(){this.dispatchCustomEvent("permissionStateUpdate",{status:this.webcam.permission})}async setStreamAndWatch(){this.webcam.watchPermission(t=>{this.onPermissionStateUpdate();let e=t==="granted";this.playOrPauseStream(e),this.showOverlay(!e,t==="denied"?"Permission denied":"Permission required"),e||this.onError(`Camera access ${t==="denied"?"denied by user":"requires permission"}`)}),this.playOrPauseStream(!0,this.autoStart)}async playOrPauseStream(t,e=!0){t&&e?this.start():this.stop()}showOverlay(t,e){this.overlayElement.style.display=t?"inline-block":"none",this.overlayElement.innerText=e??""}start(t=!1){return new Promise(async e=>{if(!this.videoElement.paused&&!t)return e(!0);this.stop();let n=await this.webcam.askAndGetStream(this.height,this.width,this.mediaDeviceId);if(!n?.active)return this.onPermissionStateUpdate(),this.showOverlay(!0,this.webcam.permission==="denied"?"Permission denied":"Permission required"),console.warn(`Camera access ${this.webcam.permission==="denied"?"denied":"required"}`),e(!1);this.videoElement.srcObject=n,this.videoElement.onloadeddata=()=>{let{HAVE_CURRENT_DATA:r,HAVE_ENOUGH_DATA:E,HAVE_FUTURE_DATA:f}=HTMLMediaElement;[r,E,f].includes(this.videoElement.readyState)&&(this.videoElement.play(),this.ready||(this.onReady(),this.ready=!0)),e(!0)}})}pause(){this.videoElement.pause()}resume(){this.videoElement.play()}stop(){let t=this.videoElement.srcObject;t?.active&&(t.getTracks().forEach(e=>e.stop()),this.videoElement.srcObject=null),this.videoElement.pause()}capture(){return new Promise(async t=>{if(!this.videoElement||this.videoElement.readyState<2)return this.onError("Camera is not ready for capturing."),t(null);this.pause(),this.videoElement.style.opacity="0.6",setTimeout(()=>{this.videoElement.style.opacity="1"},100);let e=document.createElement("canvas");e.width=this.videoElement.videoWidth,e.height=this.videoElement.videoHeight;let n=e.getContext("2d");if(!n)return this.onError("Failed to access canvas for snapshot."),t(null);n.drawImage(this.videoElement,0,0,e.width,e.height),e.toBlob(r=>{this.onCapture(r),t(r)},this.format),this.resume()})}async captureAndDownload(t=""){let e=await this.capture();if(e)try{let n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t?.length?t:`quick_snap_image_${Date.now()}`,this.containerElement.appendChild(r),r.click(),this.containerElement.removeChild(r),URL.revokeObjectURL(n)}catch(n){console.error(`QuickSnap Error: ${n}`),this.onError("Failed to capture and download snapshot.")}else this.onError("No snapshot available for download.")}async checkPermissions(){try{return(await this.webcam.fetchPermissionStatus()).state}catch(t){return console.error(`QuickSnap Error: ${t}`),this.onError("Failed to check camera permission status"),null}}async getAvailableCameras(){return(await navigator.mediaDevices.enumerateDevices()).filter(n=>n.kind==="videoinput")}};function g(){customElements.get("quick-snap")||customElements.define("quick-snap",h)}g();