@staski/vue-img-mapper
Version:
Vue Component to highlight interactive zones in images
2 lines (1 loc) • 9.12 kB
JavaScript
(function(o,d){typeof exports=="object"&&typeof module!="undefined"?module.exports=d(require("vue")):typeof define=="function"&&define.amd?define(["vue"],d):(o=typeof globalThis!="undefined"?globalThis:o||self,o["vue-img-mapper"]=d(o.Vue))})(this,function(o){"use strict";const d=(e,i)=>{i.$attrs.onImageMouseMove&&(e.preventDefault(),e.stopImmediatePropagation(),i.$emit("imageMouseMove",e))},c=(e,i)=>{i.$attrs.onImageClick&&(e.preventDefault(),e.stopImmediatePropagation(),i.$emit("imageClick",e))},p=(e,i,t,n)=>{n.$attrs.onMousemove&&(t.preventDefault(),t.stopImmediatePropagation(),n.$emit("mousemove",e,i,t))},y=(e,i,t,n)=>{n.$attrs.onMousedown&&(t.preventDefault(),t.stopImmediatePropagation(),n.$emit("mousedown",e,i,t))},C=(e,i,t,n)=>{n.$attrs.onMouseup&&(t.preventDefault(),t.stopImmediatePropagation(),n.$emit("mouseup",e,i,t))},v=(e,i,t,n)=>{n.$attrs.onTouchstart&&(t.preventDefault(),t.stopImmediatePropagation(),n.$emit("touchstart",e,i,t))},M=(e,i,t,n)=>{n.$attrs.onTouchend&&(t.preventDefault(),t.stopImmediatePropagation(),n.$emit("touchend",e,i,t))};var m=(e,i)=>{const t=e.__vccOpts||e;for(const[n,a]of i)t[n]=a;return t};const S={inheritAttrs:!1,props:{area:{type:Object,required:!0},index:{type:Number,required:!0},extendedArea:{type:Function,required:!0},hoverOff:{type:Function,required:!0},hoverOn:{type:Function,required:!0},click:{type:Function,required:!0}},methods:{mouseMove:p,mouseDown:y,mouseUp:C,touchStart:v,touchEnd:M}},k=["id","coords","shape","href"];function w(e,i,t,n,a,r){return t.area.disabled?o.createCommentVNode("",!0):(o.openBlock(),o.createElementBlock("area",{key:0,id:t.area.id,coords:t.extendedArea(t.area).scaledCoords.join(","),shape:t.area.shape,href:t.area.href,alt:"map",target:"_blank",onMouseenter:i[0]||(i[0]=s=>t.hoverOn(t.extendedArea(t.area),t.index,s)),onMouseleave:i[1]||(i[1]=s=>t.hoverOff(t.extendedArea(t.area),t.index,s)),onMousemove:i[2]||(i[2]=s=>r.mouseMove(t.extendedArea(t.area),t.index,s,this)),onMousedown:i[3]||(i[3]=s=>r.mouseDown(t.extendedArea(t.area),t.index,s,this)),onMouseup:i[4]||(i[4]=s=>r.mouseUp(t.extendedArea(t.area),t.index,s,this)),onTocuhstart:i[5]||(i[5]=s=>r.touchStart(t.extendedArea(t.area),t.index,s,this)),onTouchend:i[6]||(i[6]=s=>r.touchEnd(t.extendedArea(t.area),t.index,s,this)),onClick:i[7]||(i[7]=s=>t.click(t.extendedArea(t.area),t.index,s))},null,40,k))}var A=m(S,[["render",w]]);const u={position:"absolute",top:0,left:0},f={...u,zIndex:1,userSelect:"none"},P={...f,width:"100%",height:"auto"},W={position:"relative"},b={...u,pointerEvents:"none",zIndex:2},I=e=>e?P:f,B=e=>e&&{cursor:"pointer"}||void 0,N={src:{type:String,required:!0},map:{type:Object,required:!0},areaKeyName:{type:String,default:"id"},active:{type:Boolean,default:!0},disabled:{type:Boolean,default:!1},fillColor:{type:String,default:"rgba(255, 255, 255, 0.5)"},strokeColor:{type:String,default:"rgba(0, 0, 0, 0.5)"},lineWidth:{type:Number,default:1},imgWidth:{type:Number,default:0},width:{type:Number,default:0},height:{type:Number,default:0},natural:{type:Boolean,default:!1},stayHighlighted:{type:Boolean,default:!1},stayMultiHighlighted:{type:Boolean,default:!1},toggleHighlighted:{type:Boolean,default:!1},responsive:{type:Boolean,default:!1},parentWidth:{type:Number,default:0}},R=(e,i,t,n,a)=>{const[r,s,l,h]=e;a.fillStyle=i,a.lineWidth=t,a.strokeStyle=n,a.strokeRect(r,s,l-r,h-s),a.fillRect(r,s,l-r,h-s)},D=(e,i,t,n,a)=>{a.fillStyle=i,a.beginPath(),a.lineWidth=t,a.strokeStyle=n,a.arc(e[0],e[1],e[2],0,2*Math.PI),a.closePath(),a.stroke(),a.fill()},O=(e,i,t,n,a)=>{const r=e.reduce((s,l,h,q)=>h%2?s:[...s,q.slice(h,h+2)],[]);a.fillStyle=i,a.beginPath(),a.lineWidth=t,a.strokeStyle=n,r.forEach(s=>a.lineTo(s[0],s[1])),a.closePath(),a.stroke(),a.fill()},g=(e,i,t,n,a,r,s)=>(console.log(),e==="rect"&&r?R(i,t,n,a,s):e==="circle"&&r?D(i,t,n,a,s):e==="poly"&&r?O(i,t,n,a,s):!1),$=o.defineComponent({name:"ImageMapper",components:{RenderAreas:A},props:N,data(){return{mapState:this.map,storedMap:this.mapState,isRendered:!1,imgRef:null,ctx:null,onClick:null}},computed:{containerStyle:()=>W,canvasStyle:()=>b,mapStyle(){return B(this.onClick)},imgStyle(){return I(this.responsive)}},watch:{parentWidth(){this.responsive&&this.initCanvas()},map(e){this.setContextAndCache(e),this.updateCanvasOnMapChange()}},mounted(){this.setContextAndCache(this.map),this.updateCanvasOnMapChange()},methods:{imageMouseMove:d,imageClick:c,setContextAndCache(e){!this.isRendered&&e.areas.length&&(this.ctx=this.$refs.canvas.getContext("2d"),this.updateCacheMap(),this.isRendered=!0)},updateCanvasOnMapChange(){JSON.stringify(this.mapState)===JSON.stringify(this.map)&&(this.updateCacheMap(),this.initCanvas(),this.updateCanvas())},clearHighlightedArea(){this.mapState=this.storedMap,this.initCanvas()},extendedArea(e){const i=this.scaleCoords(e.coords),t=this.computeCenter(e);return{...e,scaledCoords:i,center:t}},updateCacheMap(){this.mapState=this.map,this.storedMap=this.map},getDimensions(e){return typeof this[e]=="function"?this[e](this.$refs.img):this[e]},getValues(e,i,t="area"){const{naturalWidth:n,naturalHeight:a,clientWidth:r,clientHeight:s}=this.$refs.img;return e==="width"?this.responsive?this.parentWidth:this.natural?n:this.width||t==="image"?i:r:e==="height"?this.responsive?s:this.natural?a:this.height||t==="image"?i:s:0},initCanvas(e=!1){const i=this.getDimensions("width"),t=this.getDimensions("height"),n=this.getValues("width",i),a=this.getValues("height",t);(this.width||this.responsive)&&(this.$refs.img.width=this.getValues("width",i,"image")),(this.height||this.responsive)&&(this.$refs.img.height=this.getValues("height",t,"image")),this.$refs.canvas.width=n,this.$refs.canvas.height=a,this.$refs.container.style.width=`${n}px`,this.$refs.container.style.height=`${a}px`,this.ctx=this.$refs.canvas.getContext("2d"),this.ctx.fillStyle=this.fillColor,this.$attrs.onLoad&&e&&this.$emit("load",this.$refs.img,{width:n,height:a}),this.imgRef=this.$refs.img,this.renderPrefilledAreas()},hoverOn(e,i,t){const{shape:n,scaledCoords:a,fillColor:r,lineWidth:s,strokeColor:l,active:h}=e;this.active&&g(n,a,r||this.fillColor,s||this.lineWidth,l||this.strokeColor,h!=null?h:!0,this.ctx),this.$attrs.onMouseenter&&(t.preventDefault(),t.stopImmediatePropagation(),this.$emit("mouseenter",e,i,t))},hoverOff(e,i,t){this.active&&(this.ctx.clearRect(0,0,this.$refs.canvas.width,this.$refs.canvas.height),this.renderPrefilledAreas()),this.$attrs.onMouseleave&&(t.preventDefault(),t.stopImmediatePropagation(),this.$emit("mouseleave",e,i,t))},click(e,i,t){var a;if(((a=e.active)!=null?a:!0)&&(this.stayHighlighted||this.stayMultiHighlighted||this.toggleHighlighted)){const r={...e},s=this.stayMultiHighlighted?this.mapState:this.storedMap;this.toggleHighlighted&&r.preFillColor?delete r.preFillColor:(this.stayHighlighted||this.stayMultiHighlighted)&&(r.preFillColor=e.fillColor||this.fillColor);const l=s.areas.map(h=>h[this.areaKeyName]===e[this.areaKeyName]?r:h);this.mapState={...this.mapState,areas:l}}this.$attrs.onClick&&(t.preventDefault(),t.stopImmediatePropagation(),this.$emit("click",e,i,t))},scaleCoords(e){const i=this.width&&this.imgWidth&&this.imgWidth>0?this.width/this.imgWidth:1;return this.responsive&&this.parentWidth?e.map(t=>t/(this.imgRef.naturalWidth/this.parentWidth)):e.map(t=>t*i)},renderPrefilledAreas(e=this.mapState){e.areas.map(i=>i.preFillColor?(g(i.shape,this.scaleCoords(i.coords),i.preFillColor,i.lineWidth||this.lineWidth,i.strokeColor||this.strokeColor,!0,this.ctx),!0):!1)},computeCenter(e){if(!e)return[0,0];const i=this.scaleCoords(e.coords);switch(e.shape){case"circle":return[i[0],i[1]];case"poly":case"rect":default:{const t=i.length/2,{y:n,x:a}=i.reduce(({y:r,x:s},l,h)=>h%2?{y:r+l/t,x:s}:{y:r,x:s+l/t},{y:0,x:0});return[a,n]}}},updateCanvas(){this.ctx.clearRect(0,0,this.$refs.canvas.width,this.$refs.canvas.height),this.renderPrefilledAreas(this.map)}}}),H=["src","useMap"],F=["name"];function E(e,i,t,n,a,r){const s=o.resolveComponent("RenderAreas");return o.openBlock(),o.createElementBlock("div",{id:"img-mapper",ref:"container",style:o.normalizeStyle(e.containerStyle)},[o.createElementVNode("img",{ref:"img",role:"presentation",class:"img-mapper-img",style:o.normalizeStyle(e.imgStyle),src:e.src,useMap:`#${e.mapState.name}`,alt:"map",onLoad:i[0]||(i[0]=l=>e.initCanvas(!0)),onClick:i[1]||(i[1]=l=>e.imageClick(l,this)),onMousemove:i[2]||(i[2]=l=>e.imageMouseMove(l,this))},null,44,H),o.createElementVNode("canvas",{ref:"canvas",class:"img-mapper-canvas",style:o.normalizeStyle(e.canvasStyle)},null,4),e.isRendered&&!e.disabled?(o.openBlock(),o.createElementBlock("map",{key:0,class:"img-mapper-map",name:e.mapState.name,style:o.normalizeStyle(e.mapStyle)},[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(e.mapState.areas,(l,h)=>(o.openBlock(),o.createBlock(s,o.mergeProps({key:l[e.areaKeyName]||h.toString(),area:l,index:h,"extended-area":e.extendedArea,"hover-on":e.hoverOn,"hover-off":e.hoverOff,click:e.click},e.$attrs),null,16,["area","index","extended-area","hover-on","hover-off","click"]))),128))],12,F)):o.createCommentVNode("",!0)],4)}var V=m($,[["render",E]]);return V});