vue-image-zoomer
Version:
Repsonsive image zoom component for Vue.js 3, that also works on touch devices. Legacy versions available for Vue 2.
2 lines (1 loc) • 10.9 kB
JavaScript
(function(l,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(l=typeof globalThis!="undefined"?globalThis:l||self,e(l.VueImageZoomer={},l.Vue))})(this,function(l,e){"use strict";var M="",c=(t,o)=>{const i=t.__vccOpts||t;for(const[h,s]of o)i[h]=s;return i};const a={name:"VueImageZoomer",emits:["onZoom","offZoom","regularLoaded","zoomLoaded","zoomLoading"],directives:{clickOutside:{mounted(t,o){t.clickOutsideEvent=function(i){t==i.target||t.contains(i.target)||o.value(i,t)},document.body.addEventListener("click",t.clickOutsideEvent)},unmounted(t){document.body.removeEventListener("click",t.clickOutsideEvent)}}},data(){return{touch:!1,zoomed:!1,x:0,y:0,touchPosition:0,origX:0,origY:0,offsetLeft:0,offsetTop:0,zoomWidth:0,zoomHeight:0,options:{zoomAmount:0,zoom:!1,zoomWebp:!1},loaded:!1,loading:!1,webp_supported:!1,cx:0,cy:0,showSlot:!0}},props:{regular:String,regularWebp:String,zoom:String,zoomWebp:String,imgClass:{type:String,default:""},alt:String,zoomAmount:{type:Number,default:0},clickZoom:Boolean,hoverMessage:{type:String,default:'<span class="vh--icon">⚲</span> Hover to zoom'},touchMessage:{type:String,default:'<span class="vh--icon">⚲</span> Tap to zoom'},clickMessage:{type:String,default:'<span class="vh--icon">⚲</span> Click to zoom'},closePos:{type:String,default:"top-left"},messagePos:{type:String,default:"bottom"},showMessage:{type:Boolean,default:!0},showMessageTouch:{type:Boolean,default:!0},tapToClose:Boolean,breakpoints:Array,touchZoomPos:{type:Array,default(){return[.5,.5]}},imgWidth:Number,imgHeight:Number,lazyload:Boolean,rightClick:{type:Boolean,default:!0}},watch:{propChanges(){this.get_options()}},computed:{propChanges(){return`${this.breakpoints}|${this.regular}|${this.regularWebp}|${this.zoom}|${this.zoomAmount}|${this.zoomWebp}|${this.lazyload}`}},mounted(){this.check_webp_feature("lossy",(t,o)=>{o&&(this.webp_supported=!0)}),this.get_options(),("ontouchstart"in window||navigator.msMaxTouchPoints)&&(this.touch=!0),this.touchLogic()},created(){window.addEventListener("resize",this.debounce(()=>{this.resize()},500))},unmounted(){window.removeEventListener("resize",this.resize())},methods:{async touchLogic(){await e.nextTick();let t,o,i=!1;this.$refs["vue-hover-zs"].addEventListener("touchstart",h=>{if(this.zoomed){h.cancelable&&h.preventDefault();let s=h.changedTouches[0];t=s.pageX-this.cx,o=s.pageY-this.cy}}),this.$refs["vue-hover-zs"].addEventListener("touchmove",h=>{if(this.zoomed){let s=h.changedTouches[0];this.x=s.pageX-t,this.y=s.pageY-o,s.pageX-t<=this.origX-this.zoomWidth&&(this.x=this.origX-this.zoomWidth),s.pageX-t>=0&&(this.x=-1),s.pageY-o<=this.origY-this.options.zoomAmount*this.origY&&(this.y=this.origY-this.options.zoomAmount*this.origY),s.pageY-o>=0&&(this.y=-1),this.touchPosition="translate3d("+this.x+"px,"+this.y+"px,0)",i=!0}}),this.$refs["vue-hover-zs"].addEventListener("touchend",h=>{if(this.zoomed){let s=h.changedTouches[0];this.cx=s.pageX-t,this.cy=s.pageY-o,!i&&this.tapToClose&&(this.zoomed=!1,this.$emit("offZoom")),i=!1}})},debounce(t,o){let i;return(...h)=>{const s=this;clearTimeout(i),i=setTimeout(()=>t.apply(s,h),o)}},get_options(){this.options.zoomAmount=this.zoomAmount,this.options.zoom=this.zoom,this.options.zoomWebp=this.zoomWebp,this.zoom||(this.options.zoom=this.regular,this.options.zoomAmount=2),!this.zoomWebp&&this.regularWebp&&(this.options.zoomWebp=this.regularWebp,this.options.zoomAmount=2),this.resize()},resize(){this.zoomed=!1,this.loaded=!1},check_webp_feature(t,o){let i={lossy:"UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA"},h=new Image;h.onload=()=>{let s=h.width>0&&h.height>0;o(t,s)},h.onerror=()=>{o(t,!1)},h.src="data:image/webp;base64,"+i[t]},loadImage(t,o){const i=new Image;i.onload=o,i.src=t},loadZoom(){this.offset(),this.options.zoomAmount!=0&&(this.zoomWidth=this.origX*this.options.zoomAmount,this.zoomHeight=this.origY*this.options.zoomAmount,this.touch&&this.mobilePos()),this.loaded?(this.zoomed=!0,this.options.zoomAmount==0&&(this.options.zoomAmount=this.zoomWidth/this.origX)):this.zoomLoad()},zoomLoad(){(!this.clickZoom||this.touch)&&(this.loading=!0,this.$emit("zoomLoading"));let t=this.options.zoom;this.breakpoints&&this.breakpoints.forEach(o=>{window.innerWidth>=o.width&&(o.zoom?t=o.zoom:t=o.regular)}),this.webp_supported&&this.options.zoomWebp&&(t=this.options.zoomWebp,this.breakpoints&&this.breakpoints.forEach(o=>{window.innerWidth>=o.width&&(o.zoomWebp?t=o.zoomWebp:t=o.regularWebp)})),this.loadImage(t,o=>{this.options.zoomAmount==0&&(this.zoomWidth=o.target.width,this.zoomHeight=o.target.height,this.options.zoomAmount=o.target.width/this.origX),this.loaded=!0,this.loading=!1,this.$emit("zoomLoaded"),(!this.clickZoom||this.touch)&&(this.zoomed=!0,this.mobilePos())})},isZoom(t,o){(o=="hover"&&!this.clickZoom&&!this.touch||o=="click"&&(this.clickZoom||this.touch)||typeof t=="object")&&(this.zoomed=!1,t==!0?(this.loadZoom(),this.$emit("onZoom")):this.$emit("offZoom"))},mobilePos(){let t=(this.zoomWidth-this.origX)*this.touchZoomPos[0],o=(this.zoomHeight-this.origY)*this.touchZoomPos[1];(this.touchZoomPos[0]>1||this.touchZoomPos[0]<0||this.touchZoomPos[1]>1||this.touchZoomPos[1]<0)&&(t=0,o=0),this.cx=-t,this.cy=-o,this.x=-t,this.y=-o,this.touchPosition="translate3d(-"+t+"px,-"+o+"px,0)"},offset(){this.origX=parseFloat(this.$refs["vue-hover-zs"].offsetWidth),this.origY=parseFloat(this.$refs["vue-hover-zs"].offsetHeight)},mousePos(t){this.offsetLeft=window.pageXOffset+this.$refs["vue-hover-zs"].getBoundingClientRect().left,this.offsetTop=window.pageYOffset+this.$refs["vue-hover-zs"].getBoundingClientRect().top,!this.touch&&!this.loading&&(this.loaded?(this.x=(t.pageX-this.offsetLeft)*(this.options.zoomAmount-1),this.y=(t.pageY-this.offsetTop)*(this.options.zoomAmount-1)):(this.offset(),this.zoomLoad()))}}},d={class:"vh--outer vh--rel"},g=["srcset","media"],u=["srcset","media"],f=["srcset"],p=["loading","src","alt","width","height"],z={key:0},k=["srcset","media"],y=["srcset","media"],w=["srcset","media"],_=["srcset","media"],B=["src"],b=["src"],A=["src"],x=["innerHTML"],W=["innerHTML"],E=["innerHTML"],v={key:1,class:"vh--loading-o vh--abs vh--flex vh--jc vh--ai"},C=[e.createElementVNode("div",{class:"vh--loading",innerHTML:"\u25E0"},null,-1)];function L(t,o,i,h,s,m){const T=e.resolveDirective("click-outside");return e.openBlock(),e.createElementBlock(e.Fragment,null,[s.showSlot&&!i.lazyload?e.renderSlot(t.$slots,"default",{key:0}):e.createCommentVNode("",!0),e.withDirectives((e.openBlock(),e.createElementBlock("div",d,[e.createElementVNode("div",{class:e.normalizeClass(["vh--holder vh--rel vh--flex vh--jc",{"vh--no-click":!i.rightClick}]),onMouseenter:o[1]||(o[1]=n=>m.isZoom(!0,"hover")),onMouseleave:o[2]||(o[2]=n=>m.isZoom(!1,"hover")),onMousemove:o[3]||(o[3]=(...n)=>m.mousePos&&m.mousePos(...n)),ref:"vue-hover-zs",onClick:o[4]||(o[4]=n=>m.isZoom(!s.zoomed,"click"))},[e.createElementVNode("picture",{class:e.normalizeClass({"vh--none":s.zoomed})},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(i.breakpoints,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.width},[n.regularWebp?(e.openBlock(),e.createElementBlock("source",{key:0,srcset:n.regularWebp,type:"image/webp",media:"(min-width:"+n.width+"px)"},null,8,g)):e.createCommentVNode("",!0),n.regular?(e.openBlock(),e.createElementBlock("source",{key:1,srcset:n.regular,media:"(min-width:"+n.width+"px)"},null,8,u)):e.createCommentVNode("",!0)],64))),128)),i.regularWebp?(e.openBlock(),e.createElementBlock("source",{key:0,srcset:i.regularWebp,type:"image/webp"},null,8,f)):e.createCommentVNode("",!0),e.createElementVNode("img",{loading:i.lazyload?"lazy":"eager",src:i.regular,class:e.normalizeClass(i.imgClass),alt:i.alt,onLoad:o[0]||(o[0]=n=>(t.$emit("regularLoaded"),s.showSlot=!1)),width:i.imgWidth,height:i.imgHeight},null,42,p)],2),s.zoomed?(e.openBlock(),e.createElementBlock("picture",z,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(i.breakpoints,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.width},[n.zoomWebp?(e.openBlock(),e.createElementBlock("source",{key:0,srcset:n.zoomWebp,type:"image/webp",media:"(min-width:"+n.width+"px)"},null,8,k)):n.regularWebp?(e.openBlock(),e.createElementBlock("source",{key:1,srcset:n.regularWebp,type:"image/webp",media:"(min-width:"+n.width+"px)"},null,8,y)):e.createCommentVNode("",!0),n.zoom?(e.openBlock(),e.createElementBlock("source",{key:2,srcset:n.zoom,media:"(min-width:"+n.width+"px)"},null,8,w)):n.regular?(e.openBlock(),e.createElementBlock("source",{key:3,srcset:n.regular,media:"(min-width:"+n.width+"px)"},null,8,_)):e.createCommentVNode("",!0)],64))),128)),s.options.zoomWebp?(e.openBlock(),e.createElementBlock("source",{key:0,src:s.options.zoomWebp,type:"image/webp"},null,8,B)):e.createCommentVNode("",!0),s.touch?(e.openBlock(),e.createElementBlock("img",{key:2,src:s.options.zoom,class:"vh--image vh--abs",style:e.normalizeStyle("width:"+s.zoomWidth+"px;transform:"+s.touchPosition)},null,12,A)):(e.openBlock(),e.createElementBlock("img",{key:1,src:s.options.zoom,class:"vh--image vh--abs",style:e.normalizeStyle({width:s.zoomWidth+"px",transform:"translate(-"+s.x+"px,-"+s.y+"px)"})},null,12,b))])):e.createCommentVNode("",!0),e.createVNode(e.Transition,{name:"VueHoverfade"},{default:e.withCtx(()=>[!s.zoomed&&!s.loading&&!i.clickZoom&&!s.touch&&i.showMessage?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["vh--message vh--abs vh--flex vh--jc vh--ai","vh--message-"+i.messagePos]),innerHTML:i.hoverMessage},null,10,x)):!s.zoomed&&!s.loading&&!s.touch&&i.showMessage?(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(["vh--message vh--abs vh--flex vh--jc vh--ai","vh--message-"+i.messagePos]),innerHTML:i.clickMessage},null,10,W)):!s.zoomed&&!s.loading&&s.touch&&i.showMessageTouch?(e.openBlock(),e.createElementBlock("div",{key:2,class:e.normalizeClass(["vh--message vh--abs vh--flex vh--jc vh--ai","vh--message-"+i.messagePos]),innerHTML:i.touchMessage},null,10,E)):e.createCommentVNode("",!0)]),_:1})],34),e.createVNode(e.Transition,{name:"VueHoverfade"},{default:e.withCtx(()=>[s.touch&&s.zoomed&&s.loaded&&!i.tapToClose?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["vh--close vh--abs vh--flex vh--jc vh--ai","vh--"+i.closePos]),onClick:o[5]||(o[5]=e.withModifiers(n=>(s.zoomed=!1,t.$emit("offZoom")),["stop"])),innerHTML:"\xD7"},null,2)):s.loading?(e.openBlock(),e.createElementBlock("div",v,C)):e.createCommentVNode("",!0)]),_:1})])),[[T,m.isZoom],[e.vShow,!s.showSlot||i.lazyload]])],64)}var r=c(a,[["render",L]]),Z={install:(t,o)=>{t.component("VueImageZoomer",r)}};l.VueImageZoomer=r,l.default=Z,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});