@lancercomet/zoom-pan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +387 -0
- package/dist/zoom-pan.d.ts +831 -0
- package/dist/zoom-pan.mjs +1194 -0
- package/dist/zoom-pan.umd.js +1 -0
- package/package.json +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(g,x){typeof exports=="object"&&typeof module<"u"?x(exports):typeof define=="function"&&define.amd?define(["exports"],x):(g=typeof globalThis<"u"?globalThis:g||self,x(g.ZoomPan={}))})(this,(function(g){"use strict";const x=async(d,t)=>new Promise((e,s)=>{const i=new Image;typeof d=="string"?(t!==void 0&&(i.crossOrigin=t),i.src=d):i.src=URL.createObjectURL(d),i.onload=()=>{e(i)},i.onerror=()=>{s(new Error("Image load failed"))}}),L=(d,t,e)=>Math.min(Math.max(d,t),e);class B{canvas;context;contentCanvas;contentContext;topScreenCanvas;topScreenContext;_render;_options;_resizeObserver;_layerManagers=[];_plugins=new Map;_isResetting=!1;_isResizing=!1;_resizeReleaseTimer;_needsRender=!0;_raf=0;_lastFrameTs=performance.now();_tx=0;_ty=0;_anchorX=0;_anchorY=0;_currentLogZ=Math.log(1);_targetLogZ=Math.log(1);LOG_MIN;LOG_MAX;_updateCallbacks=new Set;_beforeRenderCallbacks=new Set;_afterRenderCallbacks=new Set;get zoom(){return Math.exp(this._currentLogZ)}get minZoom(){return this._options.minZoom}get maxZoom(){return this._options.maxZoom}_dpr=Math.max(1,window.devicePixelRatio||1);get dpr(){return this._dpr}use(t){return this._plugins.has(t.name)?(console.warn(`Plugin "${t.name}" is already installed.`),t):(this._plugins.set(t.name,t),t.install(this),t)}unuse(t){const e=this._plugins.get(t);e&&(e.destroy(),this._plugins.delete(t))}getPlugin(t){return this._plugins.get(t)}onUpdate(t){this._updateCallbacks.add(t)}offUpdate(t){this._updateCallbacks.delete(t)}onBeforeRender(t){this._beforeRenderCallbacks.add(t)}offBeforeRender(t){this._beforeRenderCallbacks.delete(t)}onAfterRender(t){this._afterRenderCallbacks.add(t)}offAfterRender(t){this._afterRenderCallbacks.delete(t)}requestRender(){this._needsRender=!0}_clampLog(t){return L(t,this.LOG_MIN,this.LOG_MAX)}_setTargetLogZoomAtScreen(t,e,s){Number.isFinite(s)&&(this._anchorX=t,this._anchorY=e,this._targetLogZ=this._clampLog(s),this._needsRender=!0)}zoomToAtScreen(t,e,s){this._setTargetLogZoomAtScreen(t,e,Math.log(s))}zoomToAtScreenRaw(t,e,s){if(!Number.isFinite(s))return;const i=Math.max(1e-8,this._options.minZoom),n=this._options.maxZoom,a=L(s,i,n),h=Math.exp(this._currentLogZ),o=a;if(!Number.isFinite(h)||h<=0||Math.abs(o-h)<1e-12)return;const r=Math.log(a);this._currentLogZ=r,this._targetLogZ=r;const c=o/h;this._tx=t-(t-this._tx)*c,this._ty=e-(e-this._ty)*c,this._needsRender=!0}zoomToAtWorld(t,e,s){const{x:i,y:n}=this.toScreen(t,e);this.zoomToAtScreen(i,n,s)}zoomByFactorAtScreen(t,e,s){if(s<=0||!Number.isFinite(s))return;const i=Math.log(s);this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+i)}zoomByLogAtScreen(t,e,s){this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+s)}zoomByFactorAtWorld(t,e,s){const{x:i,y:n}=this.toScreen(t,e);this.zoomByFactorAtScreen(i,n,s)}zoomInAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,s=t.height/2;this.zoomByFactorAtScreen(e,s,1.2)}zoomOutAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,s=t.height/2,i=1/1.2;this.zoomByFactorAtScreen(e,s,i)}panBy(t,e){this._tx+=t,this._ty+=e,this._needsRender=!0}setPan(t,e){this._tx=t,this._ty=e,this._needsRender=!0}setTransform(t,e,s){const i=L(t,this._options.minZoom,this._options.maxZoom);this._currentLogZ=Math.log(i),this._targetLogZ=this._currentLogZ,this._tx=e,this._ty=s,this._needsRender=!0}_ensureOffscreenSizeLike(t,e){(t.width!==e.width||t.height!==e.height)&&(t.width=e.width,t.height=e.height)}_loop(){if(this._isResizing){this._raf=requestAnimationFrame(()=>this._loop());return}const t=performance.now(),e=Math.max(1,t-this._lastFrameTs);this._lastFrameTs=t;const{approachKZoom:s}=this._options,i=Math.exp(this._currentLogZ),n=this._targetLogZ-this._currentLogZ,a=Math.abs(n)>1e-6;if(a){const l=1-Math.exp(-s*e);this._currentLogZ+=n*l}const h=Math.exp(this._currentLogZ);if(h!==i){const l=this._anchorX,f=this._anchorY,p=h/i;this._tx=l-(l-this._tx)*p,this._ty=f-(f-this._ty)*p}if(this._isResetting){const l=1-Math.exp(-this._options.approachKPan*e);this._tx+=(0-this._tx)*l,this._ty+=(0-this._ty)*l;const f=Math.abs(this._currentLogZ)<.001&&Math.abs(this._targetLogZ)<1e-6,p=Math.abs(this._tx)<.5&&Math.abs(this._ty)<.5;f&&p&&(this._currentLogZ=0,this._targetLogZ=0,this._tx=0,this._ty=0,this._isResetting=!1)}for(const l of this._updateCallbacks)l(e);if(!(this._needsRender||a||this._isResetting)){this._raf=requestAnimationFrame(()=>this._loop());return}this._needsRender=!1;const r=this.contentCanvas,c=this.contentContext,_=this.topScreenCanvas,u=this.topScreenContext,m=this.canvas,y=this.context;this._ensureOffscreenSizeLike(r,m),this._ensureOffscreenSizeLike(_,m),c.setTransform(1,0,0,1,0,0),u.setTransform(1,0,0,1,0,0),y.setTransform(1,0,0,1,0,0);const v=this._options.background;typeof v=="string"&&v.trim()!==""&&v.toLowerCase()!=="transparent"?(y.fillStyle=v,y.fillRect(0,0,m.width,m.height)):y.clearRect(0,0,m.width,m.height),c.clearRect(0,0,r.width,r.height),u.clearRect(0,0,_.width,_.height),c.setTransform(this._dpr*h,0,0,this._dpr*h,this._dpr*this._tx,this._dpr*this._ty);for(const l of this._beforeRenderCallbacks)l(c);this._render(this);for(const l of this._afterRenderCallbacks)l(c);y.drawImage(r,0,0),y.drawImage(_,0,0),this._raf=requestAnimationFrame(()=>this._loop())}applyWorldTransform(t){const e=Math.exp(this._currentLogZ);t.setTransform(this._dpr*e,0,0,this._dpr*e,this._dpr*this._tx,this._dpr*this._ty)}applyScreenTransform(t){t.setTransform(this._dpr,0,0,this._dpr,0,0)}getPixelColorAtScreen(t,e){const s=Math.floor(t*this._dpr),i=Math.floor(e*this._dpr);if(s<0||i<0||s>=this.canvas.width||i>=this.canvas.height)return{r:0,g:0,b:0,a:0,rgba:"rgba(0,0,0,0)",hex:"#000000"};const a=this.contentContext.getImageData(s,i,1,1).data,h=a[0],o=a[1],r=a[2],c=a[3]/255,_=m=>m.toString(16).padStart(2,"0"),u=`#${_(h)}${_(o)}${_(r)}`;return{r:h,g:o,b:r,a:c,rgba:`rgba(${h},${o},${r},${c.toFixed(3)})`,hex:u}}getPixelColorAtWorld(t,e){const{x:s,y:i}=this.toScreen(t,e);return this.getPixelColorAtScreen(s,i)}registerLayerManager(t){t&&(this._layerManagers.includes(t)||(this._layerManagers.push(t),this._needsRender=!0))}unregisterLayerManager(t){const e=this._layerManagers.indexOf(t);e>=0&&(this._layerManagers.splice(e,1),this._needsRender=!0)}getLayerManagers(){return[...this._layerManagers]}resetSmooth(){this._isResetting=!0,this._targetLogZ=0,this._needsRender=!0}resetInstant(){this._currentLogZ=0,this._targetLogZ=0,this._tx=0,this._ty=0,this._needsRender=!0}toWorld(t,e){const s=Math.exp(this._currentLogZ),i=(t-this._tx)/s,n=(e-this._ty)/s;return{wx:i,wy:n}}toScreen(t,e){const s=Math.exp(this._currentLogZ);return{x:t*s+this._tx,y:e*s+this._ty}}getTransform(){return{zoom:Math.exp(this._currentLogZ),tx:this._tx,ty:this._ty}}getViewportBounds(){const t=Math.exp(this._currentLogZ),e=this.canvas.width/this._dpr,s=this.canvas.height/this._dpr,i=-this._tx/t,n=-this._ty/t,a=(e-this._tx)/t,h=(s-this._ty)/t;return{left:i,top:n,right:a,bottom:h,width:a-i,height:h-n}}setZoomRange(t,e){this._options.minZoom=t,this._options.maxZoom=e,this.LOG_MIN=Math.log(t),this.LOG_MAX=Math.log(e),this._targetLogZ=Math.min(this.LOG_MAX,Math.max(this.LOG_MIN,this._targetLogZ))}resizeToParent(){this._isResizing=!0;const e=(this.canvas.parentElement||this.canvas).getBoundingClientRect();this._dpr=Math.max(1,window.devicePixelRatio||1);const s=Math.max(1,Math.round(e.width)),i=Math.max(1,Math.round(e.height));this.canvas.width=Math.round(s*this._dpr),this.canvas.height=Math.round(i*this._dpr),this.canvas.style.width=`${s}px`,this.canvas.style.height=`${i}px`,this._ensureOffscreenSizeLike(this.contentCanvas,this.canvas),this._ensureOffscreenSizeLike(this.topScreenCanvas,this.canvas),clearTimeout(this._resizeReleaseTimer),this._resizeReleaseTimer=window.setTimeout(()=>{this._isResizing=!1},50)}destroy(){cancelAnimationFrame(this._raf);for(const t of this._plugins.values())t.destroy();this._plugins.clear(),this._resizeObserver&&this._resizeObserver.disconnect(),this._layerManagers=[],this._updateCallbacks.clear(),this._beforeRenderCallbacks.clear(),this._afterRenderCallbacks.clear()}constructor(t,e,s){const i=t.getContext("2d",{willReadFrequently:!0,alpha:!0});if(!i)throw new Error("2D context not available");this.canvas=t,this.context=i,this._render=e,this.contentCanvas=document.createElement("canvas"),this.contentCanvas.width=t.width,this.contentCanvas.height=t.height,this.contentContext=this.contentCanvas.getContext("2d",{alpha:!0,willReadFrequently:!0}),this.topScreenCanvas=document.createElement("canvas"),this.topScreenCanvas.width=t.width,this.topScreenCanvas.height=t.height,this.topScreenContext=this.topScreenCanvas.getContext("2d",{alpha:!0}),this._options={minZoom:.5,maxZoom:10,approachKZoom:.022,approachKPan:.022,autoResize:!0,background:"#fff",...s},this.LOG_MIN=Math.log(this._options.minZoom),this.LOG_MAX=Math.log(this._options.maxZoom),this._options.autoResize&&(this._resizeObserver=new ResizeObserver(()=>this.resizeToParent()),this._resizeObserver.observe(this.canvas.parentElement||this.canvas)),this.resizeToParent(),this._lastFrameTs=performance.now(),this._raf=requestAnimationFrame(()=>this._loop())}}let Z=0;class z{id;type;name;space="world";visible=!0;opacity=1;blend="source-over";constructor(t,e,s="world"){this.name=t,this.id=`layer_${e}_${++Z}`,this.type=e,this.space=s}}class b extends z{_redraw;canvas;context;x=0;y=0;scale=1;rotation=0;anchor="topLeft";_drawing=!1;_lastX=0;_lastY=0;beginStroke(t,e){const{lx:s,ly:i}=this.toLocalPoint(t,e);this._lastX=s,this._lastY=i,this._drawing=!0}stroke(t,e,s,i,n=1,a="brush"){if(!this._drawing)return;const{lx:h,ly:o}=this.toLocalPoint(t,e);this.context.beginPath(),this.context.moveTo(this._lastX,this._lastY),this.context.lineTo(h,o),a==="eraser"?(this.context.globalCompositeOperation="destination-out",this.context.strokeStyle="rgba(0, 0, 0, 1)"):(this.context.globalCompositeOperation="source-over",this.context.strokeStyle=s),this.context.lineWidth=i*n,this.context.lineCap="round",this.context.lineJoin="round",this.context.stroke(),this.context.closePath(),this._lastX=h,this._lastY=o}endStroke(){this._drawing=!1}isDrawing(){return this._drawing}captureSnapshot(t){try{if(t){const{x:e,y:s,width:i,height:n}=t,a=Math.max(0,Math.floor(e)),h=Math.max(0,Math.floor(s)),o=Math.min(this.canvas.width-a,Math.ceil(i)),r=Math.min(this.canvas.height-h,Math.ceil(n));return o<=0||r<=0?null:this.context.getImageData(a,h,o,r)}return this.context.getImageData(0,0,this.canvas.width,this.canvas.height)}catch{return null}}restoreSnapshot(t,e){const s=e?.x??0,i=e?.y??0;this.context.putImageData(t,s,i)}clearRegion(t){t?this.context.clearRect(t.x,t.y,t.width,t.height):this.context.clearRect(0,0,this.canvas.width,this.canvas.height)}requestRedraw(){this._redraw?.(this.context,this.canvas)}drawImage(t,e,s,i,n){this.context.drawImage(t,e,s,i??t.width,n??t.height)}hitTest(t,e){const{lx:s,ly:i}=this.toLocalPoint(t,e);return s>=0&&s<=this.canvas.width&&i>=0&&i<=this.canvas.height}toLocalPoint(t,e){const s=t-this.x,i=e-this.y,n=Math.cos(-this.rotation),a=Math.sin(-this.rotation),h=s*n-i*a,o=s*a+i*n,r=h/this.scale,c=o/this.scale,_=this.anchor==="center"?this.canvas.width/2:0,u=this.anchor==="center"?this.canvas.height/2:0;return{lx:r+_,ly:c+u}}render(t,e){if(!this.visible)return;const s=this.canvas.width*this.scale,i=this.canvas.height*this.scale,n=this.anchor==="center"?-s/2:0,a=this.anchor==="center"?-i/2:0;t.save(),t.globalAlpha=this.opacity,t.globalCompositeOperation=this.blend,t.translate(this.x,this.y),t.rotate(this.rotation),t.drawImage(this.canvas,n,a,s,i),t.restore()}cropTo(t){const e=Math.max(1,Math.floor(t.width)),s=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&s===this.canvas.height)return;const i=this._cloneCanvas(),n=Math.min(i.width,e),a=Math.min(i.height,s);this._setCanvasSize(e,s),n>0&&a>0&&this.context.drawImage(i,0,0,n,a,0,0,n,a)}resizeTo(t){const e=Math.max(1,Math.floor(t.width)),s=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&s===this.canvas.height)return;const i=this._cloneCanvas();this._setCanvasSize(e,s),i.width>0&&i.height>0&&this.context.drawImage(i,0,0,i.width,i.height,0,0,e,s)}_cloneCanvas(){const t=document.createElement("canvas");if(t.width=this.canvas.width,t.height=this.canvas.height,t.width===0||t.height===0)return t;const e=t.getContext("2d");if(!e)throw new Error("Offscreen 2D context unavailable");return e.drawImage(this.canvas,0,0),t}_setCanvasSize(t,e){this.canvas.width=t,this.canvas.height=e,this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,t,e)}destroy(){this.canvas.width=0,this.canvas.height=0}constructor(t){super(t.name||"","canvas",t.space??"world"),this.canvas=document.createElement("canvas"),this.canvas.width=t.width,this.canvas.height=t.height;const e=this.canvas.getContext("2d",{willReadFrequently:!0});if(!e)throw new Error("Offscreen 2D context unavailable");this.context=e,this.x=t.x||0,this.y=t.y||0,this.scale=t.scale??1,this.rotation=t.rotation||0,t.anchor&&(this.anchor=t.anchor),this._redraw=t.redraw,this._redraw&&this._redraw(this.context,this.canvas)}}class M extends b{#t=null;static async fromImage(t){const e=await x(t.src,t.crossOrigin),s=t.width??e.naturalWidth,i=t.height??e.naturalHeight,n=new M({name:t.name,space:t.space??"world",x:t.x??0,y:t.y??0,scale:t.scale??1,rotation:t.rotation??0,anchor:t.anchor??"topLeft",width:s,height:i});return n.context.clearRect(0,0,n.canvas.width,n.canvas.height),n.context.drawImage(e,0,0,s,i),typeof t.src!="string"&&(n.#t=e.src.startsWith("blob:")?e.src:null),n}async setSource(t,e){const s=await x(t,e);if(this.canvas.width=s.naturalWidth,this.canvas.height=s.naturalHeight,this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.drawImage(s,0,0),this.#t){try{URL.revokeObjectURL(this.#t)}catch{}this.#t=null}typeof t!="string"&&(this.#t=s.src.startsWith("blob:")?s.src:null)}paint(t){t(this.context,this.canvas)}getImageData(t=0,e=0,s=this.canvas.width,i=this.canvas.height){return this.context.getImageData(t,e,s,i)}putImageData(t,e=0,s=0){this.context.putImageData(t,e,s)}toDataURL(t="image/png",e){return this.canvas.toDataURL(t,e)}toImageBitmap(t){return createImageBitmap(this.canvas,t??{})}destroy(){if(super.destroy?.(),this.#t){try{URL.revokeObjectURL(this.#t)}catch{}this.#t=null}}constructor(t){super({name:t.name,space:t.space??"world",x:t.x,y:t.y,scale:t.scale,rotation:t.rotation,anchor:t.anchor??"topLeft",width:t.width,height:t.height}),this.type="bitmap"}}class C{_worldLayers=[];_screenLayers=[];_renderAllLayersIn(t,e){e.save(),t.applyWorldTransform(e);for(const s of this._worldLayers)!s.visible||s.opacity<=0||(e.save(),s.render(e,t),e.restore());e.restore(),e.save(),t.applyScreenTransform(e);for(const s of this._screenLayers)!s.visible||s.opacity<=0||(e.save(),s.render(e,t),e.restore());e.restore()}addLayer(t,e){const s=t.space==="world"?this._worldLayers:this._screenLayers;return typeof e=="number"&&e>=0&&e<s.length?(s.splice(e,0,t),t.id):(s.push(t),t.id)}async createImageLayer(t){const e=await M.fromImage(t);return this.addLayer(e),e}createCanvasLayer(t){const e=new b(t);return this.addLayer(e),e}removeLayer(t){const e=this._worldLayers.findIndex(i=>i.id===t);if(e>=0){this._worldLayers[e].destroy?.(),this._worldLayers.splice(e,1);return}const s=this._screenLayers.findIndex(i=>i.id===t);s>=0&&(this._screenLayers[s].destroy?.(),this._screenLayers.splice(s,1))}moveLayer(t,e){const s=this._worldLayers.findIndex(n=>n.id===t);if(s>=0){const[n]=this._worldLayers.splice(s,1),a=Math.max(0,Math.min(e,this._worldLayers.length));this._worldLayers.splice(a,0,n);return}const i=this._screenLayers.findIndex(n=>n.id===t);if(i>=0){const[n]=this._screenLayers.splice(i,1),a=Math.max(0,Math.min(e,this._screenLayers.length));this._screenLayers.splice(a,0,n)}}getLayer(t){return this._worldLayers.find(e=>e.id===t)||this._screenLayers.find(e=>e.id===t)}getAllLayers(t){return t?(t==="world"?this._worldLayers:this._screenLayers).slice():[...this._worldLayers,...this._screenLayers]}hitTest(t,e,s="world"){const i=this.getAllLayers(s);for(let n=i.length-1;n>=0;n--){const a=i[n];if(a.hitTest&&a.hitTest(t,e))return a}}destroy(){for(const t of[...this._worldLayers,...this._screenLayers])t.destroy?.();this._worldLayers=[],this._screenLayers=[]}}class I extends C{_compositeCache=null;_compositeCacheCtx=null;_compositeDirty=!0;_lastCacheWidth=0;_lastCacheHeight=0;_cachedBoundsMinX=0;_cachedBoundsMinY=0;markDirty(){this._compositeDirty=!0}addLayer(t,e){return this._compositeDirty=!0,super.addLayer(t,e)}removeLayer(t){this._compositeDirty=!0,super.removeLayer(t)}moveLayer(t,e){this._compositeDirty=!0,super.moveLayer(t,e)}renderAllLayersIn(t){const e=t.contentContext,s=this._worldLayers;s.length!==0&&(this._compositeDirty&&this._rebuildCompositeCache(s),this._compositeCache&&this._compositeCacheCtx&&e.drawImage(this._compositeCache,this._cachedBoundsMinX,this._cachedBoundsMinY))}_rebuildCompositeCache(t){let e=0,s=0,i=0,n=0,a=!1;for(const c of t){const _=c,u=_.canvas.width*_.scale,m=_.canvas.height*_.scale,y=_.anchor==="center"?-u/2:0,v=_.anchor==="center"?-m/2:0,w=_.x+y,l=_.y+v,f=w+u,p=l+m;a?(e=Math.min(e,w),s=Math.min(s,l),i=Math.max(i,f),n=Math.max(n,p)):(e=w,s=l,i=f,n=p,a=!0)}if(!a){this._compositeDirty=!1;return}const h=Math.ceil(i-e),o=Math.ceil(n-s);this._compositeCache||(this._compositeCache=document.createElement("canvas"),this._compositeCacheCtx=this._compositeCache.getContext("2d",{alpha:!0})),(this._lastCacheWidth!==h||this._lastCacheHeight!==o)&&(this._compositeCache.width=h,this._compositeCache.height=o,this._lastCacheWidth=h,this._lastCacheHeight=o),this._cachedBoundsMinX=e,this._cachedBoundsMinY=s;const r=this._compositeCacheCtx;r.clearRect(0,0,h,o),r.save(),r.translate(-e,-s);for(const c of t)!c.visible||c.opacity<=0||(r.save(),c.render(r),r.restore());r.restore(),this._compositeDirty=!1}destroy(){super.destroy(),this._compositeCache=null,this._compositeCacheCtx=null}}class E extends C{renderAllLayersIn(t){const e=t.topScreenContext;this._renderAllLayersIn(t,e)}}class T{name="interaction";_view=null;_options;_panEnabled;_zoomEnabled;_dragging=!1;_vx=0;_vy=0;_lastMoveTs=0;_activePointerId=null;_onDownBound=t=>this._onPointerDown(t);_onMoveBound=t=>this._onPointerMove(t);_onUpBound=()=>this._onPointerUp();_onWheelBound=t=>this._onWheel(t);constructor(t){this._options={panEnabled:!0,zoomEnabled:!0,friction:.92,stopSpeed:20/1e3,emaAlpha:.25,idleNoInertiaMs:120,wheelSensitivity:.0015,...t},this._panEnabled=this._options.panEnabled,this._zoomEnabled=this._options.zoomEnabled}install(t){this._view=t;const e=t.canvas;e.addEventListener("wheel",this._onWheelBound,{passive:!1}),e.addEventListener("pointerdown",this._onDownBound),window.addEventListener("pointermove",this._onMoveBound),window.addEventListener("pointerup",this._onUpBound),t.onUpdate(this._onUpdate)}destroy(){if(!this._view)return;const t=this._view.canvas;t.removeEventListener("wheel",this._onWheelBound),t.removeEventListener("pointerdown",this._onDownBound),window.removeEventListener("pointermove",this._onMoveBound),window.removeEventListener("pointerup",this._onUpBound),this._view.offUpdate(this._onUpdate),this._view=null}isPanEnabled(){return this._panEnabled}isZoomEnabled(){return this._zoomEnabled}setPanEnabled(t){this._panEnabled!==t&&(this._panEnabled=t,t||(this._dragging=!1,this._vx=0,this._vy=0))}setZoomEnabled(t){this._zoomEnabled=t}setWheelSensitivity(t){this._options.wheelSensitivity=t}isDragging(){return this._dragging}_onUpdate=t=>{if(!this._view)return;const{friction:e,stopSpeed:s}=this._options,i=Math.hypot(this._vx,this._vy)>=s;if(!this._dragging&&this._panEnabled&&i){const n=this._vx*t,a=this._vy*t;this._view.panBy(n,a),this._vx*=e,this._vy*=e,Math.hypot(this._vx,this._vy)<s&&(this._vx=0,this._vy=0)}else this._panEnabled||(this._vx=0,this._vy=0)};_onPointerDown(t){if(!(t.button!==0||!this._panEnabled)){this._dragging=!0,this._vx=0,this._vy=0,this._lastMoveTs=performance.now(),this._activePointerId=t.pointerId;try{this._view?.canvas.setPointerCapture(t.pointerId)}catch{}}}_onPointerMove(t){if(!this._dragging||!this._panEnabled||!this._view)return;const e=performance.now(),s=Math.max(1,e-(this._lastMoveTs||e-16));this._lastMoveTs=e;const i=t.movementX,n=t.movementY;this._view.panBy(i,n);const a=this._options.emaAlpha,h=i/s,o=n/s;this._vx=(1-a)*this._vx+a*h,this._vy=(1-a)*this._vy+a*o}_onPointerUp(){if(!this._dragging)return;this._dragging=!1;const t=performance.now(),e=this._lastMoveTs?t-this._lastMoveTs:1/0;if(this._activePointerId!=null&&this._view){try{this._view.canvas.releasePointerCapture(this._activePointerId)}catch{}this._activePointerId=null}if(e>=this._options.idleNoInertiaMs)this._vx=0,this._vy=0;else{const s=Math.pow(this._options.friction,e/16);this._vx*=s,this._vy*=s}Math.hypot(this._vx,this._vy)<this._options.stopSpeed&&(this._vx=0,this._vy=0)}_getLineHeightPx(){if(!this._view)return 16;const t=getComputedStyle(this._view.canvas).lineHeight;if(!t||t==="normal")return 16;const e=parseFloat(t);return Number.isFinite(e)?e:16}_normalizeWheelDelta(t){if(!this._view)return 0;let e=t.deltaY;if(t.deltaMode===1)e*=this._getLineHeightPx();else if(t.deltaMode===2){const s=this._view.canvas.clientHeight||window.innerHeight;e*=s||800}return e}_onWheel(t){if(!this._zoomEnabled||!this._view)return;t.preventDefault(),t.stopPropagation();const e=this._normalizeWheelDelta(t),s=this._view.canvas.getBoundingClientRect(),i=t.clientX-s.left,n=t.clientY-s.top;let a=-e*this._options.wheelSensitivity;t.ctrlKey||t.metaKey?a*=1.6:t.shiftKey&&(a*=.6),this._view.zoomByLogAtScreen(i,n,a)}}function A(d){return new T(d)}class k{name="document";_view=null;_options;_enabled=!1;_x=0;_y=0;_width=0;_height=0;_marginL=0;_marginR=0;_marginT=0;_marginB=0;_panClampMode="minVisible";constructor(t){this._options={rect:{x:0,y:0,width:0,height:0},margins:{},drawBorder:!1,minVisiblePx:30,panClampMode:"minVisible",...t},t?.rect&&(this._enabled=!0,this._x=t.rect.x,this._y=t.rect.y,this._width=t.rect.width,this._height=t.rect.height),t?.margins&&(this._marginL=t.margins.left??0,this._marginR=t.margins.right??0,this._marginT=t.margins.top??0,this._marginB=t.margins.bottom??0),this._panClampMode=this._options.panClampMode}install(t){this._view=t,t.onUpdate(this._onUpdate),t.onBeforeRender(this._onBeforeRender),t.onAfterRender(this._onAfterRender)}destroy(){this._view&&(this._view.offUpdate(this._onUpdate),this._view.offBeforeRender(this._onBeforeRender),this._view.offAfterRender(this._onAfterRender),this._view=null)}isEnabled(){return this._enabled}getRect(){return{x:this._x,y:this._y,width:this._width,height:this._height}}setRect(t,e,s,i){this._enabled=!0,this._x=t,this._y=e,this._width=s,this._height=i}clearRect(){this._enabled=!1}setMargins(t){this._marginL=t.left??this._marginL,this._marginR=t.right??this._marginR,this._marginT=t.top??this._marginT,this._marginB=t.bottom??this._marginB}getMargins(){return{left:this._marginL,right:this._marginR,top:this._marginT,bottom:this._marginB}}setPanClampMode(t){this._panClampMode=t}getPanClampMode(){return this._panClampMode}cropTo(t){this._doResize("crop",t)}resizeTo(t){this._doResize("resize",t)}zoomToFit(t="contain"){if(!this._enabled||!this._view)return;const e=this._view.dpr,s=this._view.canvas.width/e,i=this._view.canvas.height/e,n=Math.max(1,s-(this._marginL+this._marginR)),a=Math.max(1,i-(this._marginT+this._marginB));let h;const o=n/this._width,r=a/this._height;t==="contain"?h=Math.min(o,r):t==="cover"?h=Math.max(o,r):t==="fitWidth"?h=o:h=r,h=Math.min(this._view.maxZoom,Math.max(this._view.minZoom,h));const c=this._marginL+(n-h*this._width)/2,_=this._marginT+(a-h*this._height)/2,u=c-h*this._x,m=_-h*this._y;this._view.setTransform(h,u,m)}isPointInDocument(t,e){return this._enabled?t>=this._x&&t<=this._x+this._width&&e>=this._y&&e<=this._y+this._height:!0}_onUpdate=()=>{!this._enabled||!this._view||this._clampPan()};_onBeforeRender=t=>{!this._enabled||!this._view||(t.save(),t.beginPath(),t.rect(this._x,this._y,this._width,this._height),t.clip())};_onAfterRender=t=>{if(!(!this._enabled||!this._view)&&(t.restore(),this._options.drawBorder)){const e=this._view.zoom;t.save(),t.lineWidth=1/e,t.strokeStyle="#cfcfcf",t.strokeRect(this._x,this._y,this._width,this._height),t.restore()}};_clampPan(){if(!this._view)return;const{zoom:t,tx:e,ty:s}=this._view.getTransform(),i=t,n=this._view.dpr,a=this._view.canvas.width/n,h=this._view.canvas.height/n,o=this._x,r=this._y,c=this._x+this._width,_=this._y+this._height;let u=e,m=s;if(this._panClampMode==="margin"){const y=this._marginL-i*o,v=a-this._marginR-i*c,w=this._marginT-i*r,l=h-this._marginB-i*_,f=Math.max(1,a-(this._marginL+this._marginR)),p=Math.max(1,h-(this._marginT+this._marginB));i*this._width<=f?u=this._marginL+(f-i*this._width)/2-i*this._x:u=Math.min(y,Math.max(v,e)),i*this._height<=p?m=this._marginT+(p-i*this._height)/2-i*this._y:m=Math.min(w,Math.max(l,s))}else if(this._panClampMode==="minVisible"){const y=i*this._width,v=i*this._height,w=Math.min(this._options.minVisiblePx,y),l=Math.min(this._options.minVisiblePx,v),f=a-w-i*o,p=w-i*c,S=h-l-i*r,R=l-i*_;u=p<=f?Math.min(f,Math.max(p,e)):(p+f)/2,m=R<=S?Math.min(S,Math.max(R,s)):(R+S)/2}(u!==e||m!==s)&&this._view.setPan(u,m)}_doResize(t,e){if(!this._view)return;const s=Math.max(1,Math.floor(e.width)),i=Math.max(1,Math.floor(e.height)),n={width:s,height:i},a=this._view.getLayerManagers();for(const h of a){const o=h.getAllLayers("world");for(const r of o){const c=r;typeof c.cropTo=="function"&&typeof c.resizeTo=="function"&&(t==="crop"?c.cropTo(n):c.resizeTo(n))}}this._width=s,this._height=i,this._enabled&&this._clampPan()}}function D(d){return new k(d)}class P{type="snapshot";_target;_beforeData;_afterData;_region;_isExecuted=!0;constructor(t,e,s,i){this._target=t,this._beforeData=e,this._afterData=s,this._region=i?.region}execute(){if(this._isExecuted)return;const t=this._region?{x:this._region.x,y:this._region.y}:void 0;this._target.restoreSnapshot(this._afterData,t),this._isExecuted=!0}undo(){if(!this._isExecuted)return;const t=this._region?{x:this._region.x,y:this._region.y}:void 0;this._target.restoreSnapshot(this._beforeData,t),this._isExecuted=!1}canMerge(){return!1}merge(){return this}}function O(d,t,e,s){return!t||!e?null:new P(d,t,e,s)}class W{_maxHistorySize;_undoStack=[];_redoStack=[];executeCommand(t){t.execute(),this.addCommand(t)}addCommand(t){this._redoStack=[];const e=this._undoStack[this._undoStack.length-1];if(e&&e.canMerge?.(t)&&e.merge){const s=e.merge(t)??e;s!==e&&(this._undoStack[this._undoStack.length-1]=s);return}this._undoStack.push(t),this._undoStack.length>this._maxHistorySize&&this._undoStack.shift()}undo(){if(this._undoStack.length===0)return null;const t=this._undoStack.pop();return t.undo(),this._redoStack.push(t),t}redo(){if(this._redoStack.length===0)return null;const t=this._redoStack.pop();return t.execute(),this._undoStack.push(t),t}canUndo(){return this._undoStack.length>0}canRedo(){return this._redoStack.length>0}clear(){this._undoStack=[],this._redoStack=[]}setMaxHistorySize(t){this._maxHistorySize=Math.max(1,t),this._undoStack.length>this._maxHistorySize&&(this._undoStack=this._undoStack.slice(-this._maxHistorySize))}constructor(t){this._maxHistorySize=t?.maxHistorySize??50,this._undoStack=t?.undoStack??[],this._redoStack=t?.redoStack??[]}}g.BitmapLayer=M,g.CanvasLayer=b,g.ContentLayerManager=I,g.DocumentPlugin=k,g.HistoryManager=W,g.InteractionPlugin=T,g.LayerBase=z,g.LayerManagerBase=C,g.SnapshotCommand=P,g.TopScreenLayerManager=E,g.ViewManager=B,g.createDocumentPlugin=D,g.createInteractionPlugin=A,g.createSnapshotCommand=O,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lancercomet/zoom-pan",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Yet another web 2D rendering lib.",
|
|
5
|
+
"main": "dist/zoom-pan.umd.js",
|
|
6
|
+
"module": "dist/zoom-pan.mjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "vite",
|
|
13
|
+
"build:example": "vite build --mode example",
|
|
14
|
+
"build:lib": "vite build --mode lib"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@lancercomet/eslint-config-eslint-rules": "^0.2.0",
|
|
21
|
+
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
|
22
|
+
"stylus": "^0.64.0",
|
|
23
|
+
"typescript": "^5.9.2",
|
|
24
|
+
"vite": "^7.1.7",
|
|
25
|
+
"vite-plugin-dts": "^3.9.1",
|
|
26
|
+
"vue": "^3.5.22"
|
|
27
|
+
}
|
|
28
|
+
}
|