UNPKG

vue-picture-input

Version:

Mobile-friendly picture file input component with image preview and drag and drop.

3 lines (2 loc) 14.4 kB
(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".picture-input[data-v-29fd9cb1]{width:100%;margin:0 auto;text-align:center}.preview-container[data-v-29fd9cb1]{width:100%;box-sizing:border-box;margin:0 auto;cursor:pointer;overflow:hidden;transform:translateZ(0)}.picture-preview[data-v-29fd9cb1]{width:100%;height:100%;position:relative;z-index:10001;box-sizing:border-box;background-color:#c8c8c840}.picture-preview.dragging-over[data-v-29fd9cb1]{filter:brightness(.5)}.picture-inner[data-v-29fd9cb1]{position:relative;z-index:10002;pointer-events:none;box-sizing:border-box;margin:1em auto;padding:.5em;border:.3em dashed rgba(66,66,66,.15);border-radius:8px;width:calc(100% - 2.5em);height:calc(100% - 2.5em);display:table}.picture-inner .picture-inner-text[data-v-29fd9cb1]{display:table-cell;vertical-align:middle;text-align:center;font-size:2em;line-height:1.5}button[data-v-29fd9cb1]{margin:1em .25em;cursor:pointer}input[type=file][data-v-29fd9cb1]{display:none}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); (function(h,p){typeof exports=="object"&&typeof module<"u"?module.exports=p():typeof define=="function"&&define.amd?define(p):(h=typeof globalThis<"u"?globalThis:h||self,h.PictureInput=p())})(this,function(){"use strict";function h(t,e,s,i,a,l,n,r){var o=typeof t=="function"?t.options:t;return e&&(o.render=e,o.staticRenderFns=s,o._compiled=!0),o._scopeId="data-v-"+l,{exports:t,options:o}}const p={name:"picture-input",props:{width:{type:[String,Number],default:Number.MAX_SAFE_INTEGER},height:{type:[String,Number],default:Number.MAX_SAFE_INTEGER},margin:{type:[String,Number],default:0},accept:{type:String,default:"image/*"},capture:{type:String,default:null},size:{type:[String,Number],default:Number.MAX_SAFE_INTEGER},name:{type:String,default:null},id:{type:[String,Number],default:null},buttonClass:{type:String,default:"btn btn-primary button"},removeButtonClass:{type:String,default:"btn btn-secondary button secondary"},aspectButtonClass:{type:String,default:"btn btn-secondary button secondary"},prefill:{type:typeof File>"u"||typeof Blob>"u"?[String]:[String,File,Blob],default:""},prefillOptions:{type:Object,default:()=>({})},crop:{type:Boolean,default:!0},radius:{type:[String,Number],default:0},removable:{type:Boolean,default:!1},hideChangeButton:{type:Boolean,default:!1},autoToggleAspectRatio:{type:Boolean,default:!1},toggleAspectRatio:{type:Boolean,default:!1},changeOnClick:{type:Boolean,default:!0},plain:{type:Boolean,default:!1},zIndex:{type:Number,default:1e4},alertOnError:{type:Boolean,default:!0},customStrings:{type:Object,default:()=>({})}},watch:{prefill(){this.prefill?this.preloadImage(this.prefill,this.prefillOptions):this.removeImage()}},data(){return{imageSelected:!1,imagePreloaded:!1,previewHeight:0,previewWidth:0,draggingOver:!1,canvasWidth:0,canvasHeight:0,strings:{upload:"<p>Your device does not support file uploading.</p>",drag:"Drag an image or <br>click here to select a file",tap:"Tap here to select a photo <br>from your gallery",change:"Change Photo",aspect:"Landscape/Portrait",remove:"Remove Photo",select:"Select a Photo",selected:"<p>Photo successfully selected!</p>",fileSize:"The file size exceeds the limit",fileType:"This file type is not supported."}}},mounted(){if(this.updateStrings(),this.prefill&&this.preloadImage(this.prefill,this.prefillOptions),this.$nextTick(()=>{window.addEventListener("resize",this.onResize),this.onResize()}),this.supportsPreview){this.pixelRatio=Math.round(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI);const t=this.$refs.previewCanvas;t.getContext&&(this.context=t.getContext("2d"),this.context.scale(this.pixelRatio,this.pixelRatio))}this.accept!=="image/*"&&(this.fileTypes=this.accept.split(","),this.fileTypes=this.fileTypes.map(t=>t.trim())),this.canvasWidth=this.width!=Number.MAX_SAFE_INTEGER?this.width:this.$refs.container.clientWidth,this.canvasHeight=this.height!=Number.MAX_SAFE_INTEGER?this.height:this.canvasWidth,this.previewWidth=this.canvasWidth,this.previewHeight=this.canvasHeight},beforeUnmount(){window.removeEventListener("resize",this.onResize)},methods:{updateStrings(){for(let t in this.customStrings)t in this.strings&&typeof this.customStrings[t]=="string"&&(this.strings[t]=this.customStrings[t])},onClick(){if(!this.imageSelected){this.selectImage();return}this.changeOnClick&&this.selectImage(),this.$emit("click")},onResize(){this.resizeCanvas()&&this.imageObject&&this.drawImage(this.imageObject)},onDragEnter(){this.supportsDragAndDrop&&(this.draggingOver=!0)},onDragLeave(){this.supportsDragAndDrop&&(this.draggingOver=!1)},onFileDrop(t){this.onDragLeave(),this.$refs.fileInput.files=t.target.files||t.dataTransfer.files,this.onFileChange(t,!1)},onFileChange(t,e){const s=t.target.files||t.dataTransfer.files;if(s.length){if(s[0].size<=0||s[0].size>this.size*1024*1024){this.$emit("error",{type:"fileSize",fileSize:s[0].size,fileType:s[0].type,fileName:s[0].name,message:this.strings.fileSize+" ("+this.size+"MB)"}),this.alertOnError&&alert(this.strings.fileSize+" ("+this.size+"MB)");return}if(!(s[0].name===this.fileName&&s[0].size===this.fileSize&&this.fileModified===s[0].lastModified)){if(this.file=s[0],this.fileName=s[0].name,this.fileSize=s[0].size,this.fileModified=s[0].lastModified,this.fileType=s[0].type.split(";")[0],this.accept==="image/*"){if(this.fileType.substr(0,6)!=="image/")return}else if(this.fileTypes.indexOf(this.fileType)===-1){this.$emit("error",{type:"fileType",fileSize:this.fileSize,fileType:this.fileType,fileName:this.fileName,message:this.strings.fileType}),this.alertOnError&&alert(this.strings.fileType);return}this.imageSelected=!e,this.imagePreloaded=e,this.supportsPreview?this.loadImage(s[0],e||!1):e?this.$emit("prefill"):this.$emit("change",this.image)}}},loadImage(t,e){this.getEXIFOrientation(t,s=>{this.setOrientation(s);let i=new FileReader;i.onload=a=>{this.image=a.target.result,this.imageObject=new Image,this.imageObject.onload=()=>{if(this.autoToggleAspectRatio){let l=this.getOrientation(this.canvasWidth,this.canvasHeight),n=this.getOrientation(this.imageObject.width,this.imageObject.height);l!==n&&this.rotateCanvas()}this.drawImage(this.imageObject),e?this.$emit("prefill"):this.$emit("change",this.image)},this.imageObject.src=this.image},i.readAsDataURL(t)})},drawImage(t){this.imageWidth=t.width,this.imageHeight=t.height,this.imageRatio=t.width/t.height;let e=0,s=0,i=this.previewWidth,a=this.previewHeight;const l=this.previewWidth/this.previewHeight;this.crop?this.imageRatio>=l?(i=a*this.imageRatio,e=(this.previewWidth-i)/2):(a=i/this.imageRatio,s=(this.previewHeight-a)/2):this.imageRatio>=l?(a=i/this.imageRatio,s=(this.previewHeight-a)/2):(i=a*this.imageRatio,e=(this.previewWidth-i)/2);const n=this.$refs.previewCanvas;n.style.background="none",n.width=this.previewWidth*this.pixelRatio,n.height=this.previewHeight*this.pixelRatio,this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,n.width,n.height),this.rotate&&typeof this.imageObject.style.imageOrientation>"u"&&(this.context.translate(e*this.pixelRatio,s*this.pixelRatio),this.context.translate(i/2*this.pixelRatio,a/2*this.pixelRatio),this.context.rotate(this.rotate),e=-i/2,s=-a/2),this.context.drawImage(t,e*this.pixelRatio,s*this.pixelRatio,i*this.pixelRatio,a*this.pixelRatio)},selectImage(){this.$refs.fileInput.click()},removeImage(){this.$refs.fileInput.value="",this.$refs.fileInput.type="",this.$refs.fileInput.type="file",this.fileName="",this.fileType="",this.fileSize=0,this.fileModified=0,this.imageSelected=!1,this.image="",this.file=null,this.imageObject=null,this.$refs.previewCanvas&&(this.$refs.previewCanvas.style.backgroundColor="rgba(200,200,200,.25)",this.$refs.previewCanvas.width=this.previewWidth*this.pixelRatio),this.$emit("remove")},rotateImage(){this.rotateCanvas(),this.imageObject&&this.drawImage(this.imageObject);let t=this.getOrientation(this.canvasWidth,this.canvasHeight);this.$emit("aspectratiochange",t)},resizeCanvas(){let t=this.canvasWidth/this.canvasHeight,e=this.$refs.container.clientWidth;return!e||!this.toggleAspectRatio&&!this.autoToggleAspectRatio&&e===this.containerWidth?!1:(this.containerWidth=e,this.previewWidth=Math.min(this.containerWidth-this.margin*2,this.canvasWidth),this.previewHeight=this.previewWidth/t,!0)},getOrientation(t,e){let s="square";return t>e?s="landscape":t<e&&(s="portrait"),s},switchCanvasOrientation(){const t=this.canvasWidth,e=this.canvasHeight;this.canvasWidth=e,this.canvasHeight=t},rotateCanvas(){this.switchCanvasOrientation(),this.resizeCanvas()},setOrientation(t){this.rotate=!1,t===8?this.rotate=-Math.PI/2:t===6?this.rotate=Math.PI/2:t===3&&(this.rotate=-Math.PI)},getEXIFOrientation(t,e){var s=new FileReader;s.onload=i=>{var a=new DataView(i.target.result);if(a.getUint16(0,!1)!==65496)return e(-2);for(var l=a.byteLength,n=2;n<l;){var r=a.getUint16(n,!1);if(n+=2,r===65505){if(a.getUint32(n+=2,!1)!==1165519206)return e(-1);var o=a.getUint16(n+=6,!1)===18761;n+=a.getUint32(n+4,o);var c=a.getUint16(n,o);n+=2;for(var f=0;f<c;f++)if(a.getUint16(n+f*12,o)===274)return e(a.getUint16(n+f*12+8,o))}else{if((r&65280)!==65280)break;n+=a.getUint16(n,!1)}}return e(-1)},s.readAsArrayBuffer(t.slice(0,65536))},preloadImage(t,e){let s=window.File;try{new s([],"")}catch{s=class extends Blob{constructor(n,r,o={}){super(n,o),this.lastModifiedDate=new Date,this.lastModified=+this.lastModifiedDate,this.name=r}}}if(e=Object.assign({},e),typeof t=="object"){this.imagePreloaded=!0,this.supportsPreview?this.loadImage(t,!0):this.$emit("prefill");return}t.indexOf("data:")===-1&&(t.indexOf("?")!==-1?t+="&_="+new Date().getTime():t+="?_="+new Date().getTime());let i=new Headers;i.append("Accept","image/*"),fetch(t,{method:"GET",mode:"cors",headers:i}).then(a=>a.blob()).then(a=>{let l={target:{files:[]}};const n=e.fileName||t.split("/").slice(-1)[0];let r=e.mediaType||a.type||"image/"+(e.fileType||n.split("?")[0].split(".").slice(-1)[0].split("?")[0]);r=r.replace("jpg","jpeg"),r=r.replace("image/svg","image/svg+xml"),r==="image/svg"&&(r="image/svg+xml"),l.target.files[0]=new s([a],n,{type:r}),this.onFileChange(l,!0)}).catch(a=>{this.$emit("error",{type:"failedPrefill",message:"Failed loading prefill image: "+a}),this.alertOnError&&alert("Failed loading prefill image: "+a)})}},computed:{supportsUpload(){if(navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/))return!1;const t=document.createElement("input");return t.type="file",!t.disabled},supportsPreview(){return window.FileReader&&!!window.CanvasRenderingContext2D},supportsDragAndDrop(){const t=document.createElement("div");return("draggable"in t||"ondragstart"in t&&"ondrop"in t)&&!("ontouchstart"in window||navigator.msMaxTouchPoints)},computedClasses(){const t={};return t["dragging-over"]=this.draggingOver,t},fontSize(){return Math.min(.04*this.previewWidth,21)+"px"}}};var g=function(){var e=this,s=e._self._c;return s("div",{ref:"container",staticClass:"picture-input",attrs:{id:"picture-input"}},[e.supportsUpload?e.supportsPreview?s("div",[s("div",{staticClass:"preview-container",style:{maxWidth:e.previewWidth+"px",height:e.previewHeight+"px",borderRadius:e.radius+"%"}},[s("canvas",{ref:"previewCanvas",staticClass:"picture-preview",class:e.computedClasses,style:{height:e.previewHeight+"px",zIndex:parseInt(e.zIndex)+1},attrs:{tabindex:"0"},on:{drag:function(i){i.stopPropagation(),i.preventDefault()},dragover:function(i){i.stopPropagation(),i.preventDefault()},dragstart:function(i){i.stopPropagation(),i.preventDefault()},dragend:function(i){i.stopPropagation(),i.preventDefault()},dragenter:function(i){return i.stopPropagation(),i.preventDefault(),e.onDragEnter.apply(null,arguments)},dragleave:function(i){return i.stopPropagation(),i.preventDefault(),e.onDragLeave.apply(null,arguments)},drop:function(i){return i.stopPropagation(),i.preventDefault(),e.onFileDrop.apply(null,arguments)},click:function(i){return i.preventDefault(),e.onClick.apply(null,arguments)},keyup:function(i){return!i.type.indexOf("key")&&e._k(i.keyCode,"enter",13,i.key,"Enter")?null:e.onClick.apply(null,arguments)}}}),!(e.imageSelected||e.imagePreloaded)&&!e.plain?s("div",{staticClass:"picture-inner",style:{top:-e.previewHeight+"px",marginBottom:-e.previewHeight+"px",fontSize:e.fontSize,borderRadius:e.radius+"%",zIndex:parseInt(e.zIndex)+2}},[e.supportsDragAndDrop?s("span",{staticClass:"picture-inner-text",domProps:{innerHTML:e._s(e.strings.drag)}}):s("span",{staticClass:"picture-inner-text",domProps:{innerHTML:e._s(e.strings.tap)}})]):e._e()]),e.imageSelected&&!e.hideChangeButton?s("button",{class:e.buttonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.selectImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.change)+" ")]):e._e(),e.imageSelected&&e.removable?s("button",{class:e.removeButtonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.removeImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.remove)+" ")]):e._e(),e.imageSelected&&e.toggleAspectRatio&&e.width!==e.height?s("button",{class:e.aspectButtonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.rotateImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.aspect)+" ")]):e._e()]):s("div",[e.imageSelected?s("div",[s("div",{domProps:{innerHTML:e._s(e.strings.selected)}}),e.hideChangeButton?e._e():s("button",{class:e.buttonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.selectImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.change)+" ")]),e.removable?s("button",{class:e.removeButtonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.removeImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.remove)+" ")]):e._e()]):s("button",{class:e.buttonClass,attrs:{type:"button"},on:{click:function(i){return i.preventDefault(),e.selectImage.apply(null,arguments)}}},[e._v(" "+e._s(e.strings.select)+" ")])]):s("div",{domProps:{innerHTML:e._s(e.strings.upload)}}),s("input",{ref:"fileInput",attrs:{type:"file",name:e.name,id:e.id,accept:e.accept,capture:e.capture},on:{change:function(i){return i.stopPropagation(),e.onFileChange.apply(null,arguments)}}})])},d=[],u=h(p,g,d,!1,null,"29fd9cb1");return u.exports});