vue-grabbing-box
Version:
vue-grabbing-box is a container component that allows you to grab to change view-box and also with scaling
6 lines (5 loc) • 13.8 kB
JavaScript
(function(c,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(c=typeof globalThis<"u"?globalThis:c||self,f(c.GrabbingBox={}))})(this,function(c){"use strict";var f=document.createElement("style");f.textContent=`.relative{position:relative}.flex{display:flex}.h-full{height:100%}.w-full{width:100%}.cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}.flex-col{flex-direction:column}.overflow-hidden{overflow:hidden}.items-center{align-items:center}.justify-center{justify-content:center}.scale-btn-group[data-v-0123c1b8]{position:absolute;top:20px;right:10px;z-index:999;user-select:none}.scale-button[data-v-0123c1b8]{height:28px;text-align:center;color:#606266;background:#fff;border:1px solid #dcdfe6;cursor:pointer;box-sizing:border-box;vertical-align:middle;outline:none}.scale-button[data-v-0123c1b8]:hover:not(.is-disabled){color:#409eff;border-color:#c6e2ff;background-color:#ecf5ff}.scale-button[data-v-0123c1b8]:active:not(.is-disabled){color:#3a8ee6;border-color:#3a8ee6;outline:0}.scale-button.is-disabled[data-v-0123c1b8]{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5}.scale-button.btn-plus[data-v-0123c1b8],.scale-button.btn-minus[data-v-0123c1b8]{width:28px;font-size:1.25rem;border-radius:50%;line-height:24px}.scale-button.btn-reset[data-v-0123c1b8]{padding:5px 15px;font-size:12px;border-radius:2px;margin-left:10px}.grabbing-box-container .cursor-grab[data-v-ef31f3e2],.grabbing-box-container .cursor-grab *[data-v-ef31f3e2] :not(:hover){cursor:grab}.grabbing-box-container .cursor-grabbing[data-v-ef31f3e2],.grabbing-box-container .cursor-grabbing *[data-v-ef31f3e2]{cursor:grabbing}.grabbing-box-container .scale-button-group[data-v-ef31f3e2]{--scale-buttons-space-x: 10px;--scale-buttons-space-y: 20px}.grabbing-box-container .scale-button-group.custom-position-top[data-v-ef31f3e2]{top:var(--scale-buttons-space-y);bottom:unset}.grabbing-box-container .scale-button-group.custom-position-bottom[data-v-ef31f3e2]{bottom:var(--scale-buttons-space-y);top:unset}.grabbing-box-container .scale-button-group.custom-position-left[data-v-ef31f3e2]{left:var(--scale-buttons-space-x);right:unset}.grabbing-box-container .scale-button-group.custom-position-right[data-v-ef31f3e2]{right:var(--scale-buttons-space-x);left:unset}.grabbing-box-container .grabbing-box *[data-v-ef31f3e2]{-webkit-user-drag:none}
`,document.head.appendChild(f);const I="",D="1.0.0";function b(){var t=navigator.userAgent,e=["Android","iPhone","SymbianOS","Windows Phone","iPad","iPod"];return!e.some(function(s){return t.includes(s)})}function w(t){var e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:50,s=0;return function(){if(typeof t!="function")throw new Error("".concat(t," is not a function"));var a=this;if(!(Date.now()-s<e)){s=Date.now();for(var n=arguments.length,o=new Array(n),i=0;i<n;i++)o[i]=arguments[i];return t.apply(a,o)}}}var $=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"scale-btn-group flex items-center"},[s("div",{staticClass:"scale-button btn-plus flex justify-center items-center",class:{"is-disabled":t.scaleLimitOverMax},on:{click:function(a){return a.stopPropagation(),t.zoomIn.apply(null,arguments)}}},[s("svg",{attrs:{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24"}},[s("path",{attrs:{fill:"currentColor",d:"M18 12.998h-5v5a1 1 0 0 1-2 0v-5H6a1 1 0 0 1 0-2h5v-5a1 1 0 0 1 2 0v5h5a1 1 0 0 1 0 2z"}})])]),s("div",{staticStyle:{display:"inline-block",margin:"0 8px"}},[t._v(t._s(t.scaling)+"%")]),s("div",{staticClass:"scale-button btn-minus flex justify-center items-center",class:{"is-disabled":t.scaleLimitBelowMin},on:{click:function(a){return a.stopPropagation(),t.zoomOut.apply(null,arguments)}}},[s("svg",{attrs:{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24"}},[s("path",{attrs:{fill:"currentColor",d:"M18 12.998H6a1 1 0 0 1 0-2h12a1 1 0 0 1 0 2z"}})])]),s("div",{staticClass:"scale-button btn-reset",on:{click:function(a){return a.stopPropagation(),t.reset.apply(null,arguments)}}},[t._v("重置")])])},B=[];const L="";function v(t,e,s,a,n,o,i,p){var r=typeof t=="function"?t.options:t;e&&(r.render=e,r.staticRenderFns=s,r._compiled=!0),a&&(r.functional=!0),o&&(r._scopeId="data-v-"+o);var u;if(i?(u=function(l){l=l||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,!l&&typeof __VUE_SSR_CONTEXT__<"u"&&(l=__VUE_SSR_CONTEXT__),n&&n.call(this,l),l&&l._registeredComponents&&l._registeredComponents.add(i)},r._ssrRegister=u):n&&(u=p?function(){n.call(this,(r.functional?this.parent:this).$root.$options.shadowRoot)}:n),u)if(r.functional){r._injectStyles=u;var N=r.render;r.render=function(k,T){return u.call(T),N(k,T)}}else{var y=r.beforeCreate;r.beforeCreate=y?[].concat(y,u):[u]}return{exports:t,options:r}}const P={name:"ScaleButtonGroup",props:{scaling:{type:Number,default:100},maxScale:{type:Number,default:200},minScale:{type:Number,default:20}},computed:{scaleLimitOverMax(){return this.scaling>=this.maxScale},scaleLimitBelowMin(){return this.scaling<=this.minScale}},methods:{zoomIn(){this.$emit("zoom-in")},zoomOut(){this.$emit("zoom-out")},reset(){this.$emit("reset")}}},x={};var C=v(P,$,B,!1,E,"0123c1b8",null,null);function E(t){for(let e in x)this[e]=x[e]}const M=function(){return C.exports}();var R=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{ref:"containerRef",staticClass:"grabbing-box-container w-full h-full overflow-hidden flex flex-col relative",class:{"cursor-grab":!t.dragging,"cursor-grabbing":t.dragging}},[t.scaleButtons?s("scale-button-group",{staticClass:"scale-button-group",class:{"custom-position-top":t.safeScaleButtonsPosition.top,"custom-position-bottom":t.safeScaleButtonsPosition.bottom,"custom-position-left":t.safeScaleButtonsPosition.left,"custom-position-right":t.safeScaleButtonsPosition.right},style:`
--scale-buttons-space-x: ${t.scaleButtonsSpaceX};
--scale-buttons-space-y: ${t.scaleButtonsSpaceY};
`,attrs:{scaling:t.scaling},on:{"zoom-in":t.zoomIn,"zoom-out":t.zoomOut,reset:t.reset}}):t._e(),s("div",{ref:"contentBoxRef",staticClass:"grabbing-box w-full h-full",staticStyle:{"transform-origin":"center center","user-select":"none",transform:"matrix(1, 0, 0, 1, 0, 0)"}},[t._t("default")],2)],1)},z=[];const j="",g=1,m=6,S=1,X={name:"GrabbingBox",props:{useCapture:{type:Boolean,default:!0},maxScale:{type:Number,default:200},minScale:{type:Number,default:20},initScale:{type:Number,default:100},scaleButtons:{type:Boolean,default:!0},scaleButtonsPosition:{type:String,default:"top right"},scaleButtonsSpaceX:{type:String,default:"10px"},scaleButtonsSpaceY:{type:String,default:"20px"},scaleStep:{type:Number,default:10},throttleSpan:{type:Number,default:50},scrollSpeed:{type:Number,default:S,validator(t){return g<=t&&t<=m}},emitClickOnDrag:{type:Boolean,default:!1}},components:{ScaleButtonGroup:M},mounted(){this.init()},beforeDestroy(){Object.entries(this.getContainerEvents()).forEach(t=>{const[e,s]=t;this.unbindEvent(this.$refs.containerRef,e,s)}),Object.entries(this.getBodyEvents()).forEach(t=>{const[e,s]=t;this.unbindEvent(document,e,s)})},computed:{scaling(){const{scale:t}=this.lastTransformData;return parseInt(t*1e4/100)},safeScrollSpeed(){return isNaN(this.scrollSpeed)?S:g>this.scrollSpeed?g:m<this.scrollSpeed?m:this.scrollSpeed},safeScaleButtonsPosition(){const t=(this.scaleButtonsPosition||"").trim().toLowerCase();if(!t)return{right:!0,top:!0,left:!1,bottom:!1};const e=t.includes("left")?"left":"right",s=t.includes("bottom")?"bottom":"top";return{top:s==="top",bottom:s==="bottom",left:e==="left",right:e==="right"}}},data(){return{lastTransformData:{scale:1,translateX:0,translateY:0},lastPosition:{x:null,y:null,distance:0},readyToDrag:!1,dragging:!1,readyToScale:!1,isScaling:!1,scaleTranslateProportion:[0,0],dragged:!1}},methods:{useInitSacle(){if(this.maxScale<this.minScale)throw new Error("[Props Error]maxScale should greater than minSacle");let t=this.initScale;t>this.maxScale?t=this.maxScale:t<this.minScale&&(t=this.minScale),this.lastTransformData.scale=this.ensureScaleInRange(t/100),this.setMatrix(this.$refs.contentBoxRef)},init(){this.useInitSacle();const t=this.getBodyEvents();Object.entries(t).forEach(a=>{const[n,o]=a;this.bindEvent(document,n,o,{passive:!1,capture:!1})});const e=this.getContainerEvents(),s=Object.keys(t);Object.entries(e).forEach(a=>{const[n,o]=a,i=s.includes(n)?o:w(o,this.throttleSpan);this.bindEvent(this.$refs.containerRef,n,i,{capture:this.useCapture,passive:!1})})},getBodyEvents(){return b()?{wheel:t=>{t.ctrlKey&&this.disableDefaultBehavior(t)}}:{touchmove:this.disableDefaultBehavior}},getContainerEvents(){return b()?{mousedown:this.onMouseDown,mousemove:this.onMouseMove,mouseup:this.onMouseUp,mouseleave:this.onMouseLeave,wheel:this.onWheel}:{touchstart:this.onTouchStart,touchmove:this.onTouchMove,touchend:this.onTouchEnd}},disableDefaultBehavior(t){t.preventDefault()},reset(){this.lastTransformData={scale:1,translateX:0,translateY:0},this.setMatrix(this.$refs.contentBoxRef),this.$emit("reset")},zoomIn(){const{scale:t}=this.lastTransformData,e=(t*100+this.scaleStep)/100;this.lastTransformData.scale=Math.min(e,this.maxScale/100),this.setMatrix(this.$refs.contentBoxRef),this.$emit("zoom-in",this.lastTransformData)},zoomOut(){const{scale:t}=this.lastTransformData,e=(t*100-this.scaleStep)/100;this.lastTransformData.scale=Math.max(e,this.minScale/100),this.setMatrix(this.$refs.contentBoxRef),this.$emit("zoom-out",this.lastTransformData)},bindEvent(t,e,s,a){t.addEventListener(e,s,a)},unbindEvent(t,e,s){t.removeEventListener(e,s)},move(t,e){const{translateX:s,translateY:a}=this.lastTransformData,{deltaX:n,deltaY:o}=e,[i,p]=[s+n,a+o];this.lastTransformData={...this.lastTransformData,translateX:i,translateY:p},this.setMatrix(t),this.$emit("move",this.lastTransformData)},setMatrix(t){const{scale:e,translateX:s,translateY:a}=this.lastTransformData,n=this.ensureScaleInRange(e);t.style.transform=`matrix(${n}, 0, 0, ${n}, ${s}, ${a})`,this.lastTransformData.scale=n,this.$emit("update",this.lastTransformData)},handleInnerClick(t){!this.emitClickOnDrag&&this.dragged&&(t.stopPropagation(),t.preventDefault())},onMouseDown(t){this.readyToDrag=!0,this.lastPosition={x:t.clientX,y:t.clientY},this.emitClickOnDrag||(this.dragged=!1,this.bindEvent(this.$refs.containerRef,"click",this.handleInnerClick,{capture:!0}))},onMouseMove(t){if(this.readyToDrag){t.stopPropagation(),t.preventDefault(),this.dragging=!0;const{clientX:e,clientY:s}=t,{x:a,y:n}=this.lastPosition,[o,i]=[e-a,s-n];this.move(this.$refs.contentBoxRef,{deltaX:o,deltaY:i}),this.lastPosition={x:e,y:s},this.emitClickOnDrag||(this.dragged=!0)}},onMouseUp(){this.readyToDrag=!1,this.dragging=!1,this.unbindEvent(this.$refs.containerRef,"click",this.handleInnerClick)},onMouseLeave(){},onWheel(t){const{ctrlKey:e,wheelDelta:s}=t;if(t.preventDefault(),t.stopPropagation(),e)s>0?this.zoomIn():this.zoomOut();else{const{deltaMode:a,wheelDeltaX:n,wheelDeltaY:o}=t;a===0&&(this.lastTransformData.translateX+=n,this.lastTransformData.translateY+=o),this.setMatrix(this.$refs.contentBoxRef)}},onTouchStart(t){if(t.touches.length===1){const{clientX:e,clientY:s}=t.touches[0];this.lastPosition={x:e,y:s},this.readyToDrag=!0,this.dragging=!1,this.readyToScale=!1}else if(t.touches.length===2){this.readyToScale=!0,this.readyToDrag=!1,this.dragging=!1;const[e,s]=t.touches,a=e.clientX-s.clientX,n=e.clientY-s.clientY;this.lastPosition.distance=Math.sqrt(a*a+n*n);const o=[(e.clientX+a/2-this.lastTransformData.x)/this.lastTransformData.scale,(e.clientY+n/2-this.lastTransformData.y)/this.lastTransformData.scale];this.scaleTranslateProportion=[o[0]/this.$refs.contentBoxRef.offsetWidth,o[1]/this.$refs.contentBoxRef.offsetHeight]}},onTouchMove(t){if(t.preventDefault(),t.stopPropagation(),t.touches.length===1&&this.readyToDrag){this.dragging=!0;const{clientX:e,clientY:s}=t.touches[0],{x:a,y:n}=this.lastPosition,[o,i]=[e-a,s-n];this.move(this.$refs.contentBoxRef,{deltaX:o,deltaY:i}),this.lastPosition={...this.lastPosition,x:e,y:s}}else if(t.touches.length===2){if(!this.readyToScale){this.onTouchStart(t);return}const[e,s]=t.touches,a=Math.sqrt((e.clientX-s.clientX)*(e.clientX-s.clientX)+(e.clientY-s.clientY)*(e.clientY-s.clientY));this.getScaling(a/this.lastPosition.distance,!1),this.lastPosition.distance=a}},onTouchEnd(t){this.readyToDrag=!1,this.dragging=!1,this.readyToScale=!1,this.isScaling=!1,this.lastPosition={x:0,y:0,distance:0}},getScaling(t,e=!0){if(t===0&&t===1)return;const s=[this.$refs.contentBoxRef.offsetWidth*this.lastTransformData.scale,this.$refs.contentBoxRef.offsetHeight*this.lastTransformData.scale];let a=this.scaleTranslateProportion;if(e){const n=[(this.$refs.containerRef.offsetWidth/2-this.lastTransformData.x)/this.lastTransformData.scale,(this.$refs.containerRef.offsetHeight/2-this.lastTransformData.y)/this.lastTransformData.scale];a=[n[0]/this.$refs.containerRef.offsetWidth,n[1]/this.$refs.containerRef.offsetHeight]}this.lastTransformData.x+=s[0]*(1-t)*a[0]||0,this.lastTransformData.y+=s[1]*(1-t)*a[1]||0,this.lastTransformData.scale*=t,this.setMatrix(this.$refs.contentBoxRef)},ensureScaleInRange(t){return t*100>this.maxScale?t=this.maxScale/100:t*100<this.minScale&&(t=this.minScale/100),t}}},_={};var O=v(X,R,z,!1,Y,"ef31f3e2",null,null);function Y(t){for(let e in _)this[e]=_[e]}const h=function(){return O.exports}();h.install=function(t){t.component(h.name,h)};var d=function(e){d.installed||(d.installed=!0,e.component(h.name,h))};typeof window<"u"&&window.Vue&&d(window.Vue),c.GrabbingBox=h,c.default=d,c.version=D,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});