@lancercomet/zoom-pan 0.1.0 → 0.1.2

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.
@@ -0,0 +1,214 @@
1
+ import type { LayerManagerBase } from '../layer/layer-manager.base';
2
+ import type { Plugin } from '../plugins/types';
3
+ type RenderFn = (view: ViewManager) => void;
4
+ type UpdateCallback = (dt: number) => void;
5
+ type RenderCallback = (ctx: CanvasRenderingContext2D) => void;
6
+ interface ViewManagerOption {
7
+ minZoom?: number;
8
+ maxZoom?: number;
9
+ approachKZoom?: number;
10
+ approachKPan?: number;
11
+ autoResize?: boolean;
12
+ background?: string | null;
13
+ }
14
+ declare class ViewManager {
15
+ readonly canvas: HTMLCanvasElement;
16
+ readonly context: CanvasRenderingContext2D;
17
+ readonly contentCanvas: HTMLCanvasElement;
18
+ readonly contentContext: CanvasRenderingContext2D;
19
+ readonly topScreenCanvas: HTMLCanvasElement;
20
+ readonly topScreenContext: CanvasRenderingContext2D;
21
+ private readonly _render;
22
+ private readonly _options;
23
+ private readonly _resizeObserver?;
24
+ private _layerManagers;
25
+ private _plugins;
26
+ private _isResetting;
27
+ private _isResizing;
28
+ private _resizeReleaseTimer;
29
+ private _needsRender;
30
+ private _raf;
31
+ private _lastFrameTs;
32
+ private _tx;
33
+ private _ty;
34
+ private _anchorX;
35
+ private _anchorY;
36
+ private _currentLogZ;
37
+ private _targetLogZ;
38
+ private LOG_MIN;
39
+ private LOG_MAX;
40
+ private _updateCallbacks;
41
+ private _beforeRenderCallbacks;
42
+ private _afterRenderCallbacks;
43
+ get zoom(): number;
44
+ get minZoom(): number;
45
+ get maxZoom(): number;
46
+ private _dpr;
47
+ get dpr(): number;
48
+ /**
49
+ * Install a plugin.
50
+ */
51
+ use<T extends Plugin>(plugin: T): T;
52
+ /**
53
+ * Uninstall a plugin by name.
54
+ */
55
+ unuse(name: string): void;
56
+ /**
57
+ * Get an installed plugin by name.
58
+ */
59
+ getPlugin<T extends Plugin>(name: string): T | undefined;
60
+ /**
61
+ * Register a callback to be called every frame with delta time.
62
+ */
63
+ onUpdate(callback: UpdateCallback): void;
64
+ /**
65
+ * Unregister an update callback.
66
+ */
67
+ offUpdate(callback: UpdateCallback): void;
68
+ /**
69
+ * Register a callback to be called before rendering (with world transform applied).
70
+ */
71
+ onBeforeRender(callback: RenderCallback): void;
72
+ /**
73
+ * Unregister a before-render callback.
74
+ */
75
+ offBeforeRender(callback: RenderCallback): void;
76
+ /**
77
+ * Register a callback to be called after rendering.
78
+ */
79
+ onAfterRender(callback: RenderCallback): void;
80
+ /**
81
+ * Unregister an after-render callback.
82
+ */
83
+ offAfterRender(callback: RenderCallback): void;
84
+ /**
85
+ * Request a render on the next frame.
86
+ * Call this when content changes and needs to be redrawn.
87
+ */
88
+ requestRender(): void;
89
+ /**
90
+ * Clamp target zoom (log space) to range.
91
+ */
92
+ private _clampLog;
93
+ /**
94
+ * Set target zoom at screen anchor point, with smooth transition.
95
+ */
96
+ private _setTargetLogZoomAtScreen;
97
+ /**
98
+ * Zoom to absolute zoom level at screen point, with smooth transition.
99
+ */
100
+ zoomToAtScreen(anchorX: number, anchorY: number, zoom: number): void;
101
+ /**
102
+ * Zoom to absolute zoom level at screen point, instantly (no animation).
103
+ */
104
+ zoomToAtScreenRaw(anchorX: number, anchorY: number, zoom: number): void;
105
+ /**
106
+ * Zoom to absolute zoom level at world point, with smooth transition.
107
+ */
108
+ zoomToAtWorld(wx: number, wy: number, zoom: number): void;
109
+ /**
110
+ * Zoom by multiplicative factor at screen point, with smooth transition.
111
+ */
112
+ zoomByFactorAtScreen(anchorX: number, anchorY: number, factor: number): void;
113
+ /**
114
+ * Zoom by log step at screen point. Used by interaction plugin.
115
+ */
116
+ zoomByLogAtScreen(anchorX: number, anchorY: number, stepLog: number): void;
117
+ /**
118
+ * Zoom by multiplicative factor at world point, with smooth transition.
119
+ */
120
+ zoomByFactorAtWorld(wx: number, wy: number, factor: number): void;
121
+ zoomInAtCenter(): void;
122
+ zoomOutAtCenter(): void;
123
+ /**
124
+ * Pan by delta (CSS px).
125
+ */
126
+ panBy(dx: number, dy: number): void;
127
+ /**
128
+ * Set pan position (CSS px).
129
+ */
130
+ setPan(tx: number, ty: number): void;
131
+ /**
132
+ * Set full transform: zoom and pan.
133
+ */
134
+ setTransform(zoom: number, tx: number, ty: number): void;
135
+ private _ensureOffscreenSizeLike;
136
+ private _loop;
137
+ applyWorldTransform(ctx: CanvasRenderingContext2D): void;
138
+ applyScreenTransform(ctx: CanvasRenderingContext2D): void;
139
+ getPixelColorAtScreen(sx: number, sy: number): {
140
+ r: number;
141
+ g: number;
142
+ b: number;
143
+ a: number;
144
+ rgba: string;
145
+ hex: string;
146
+ };
147
+ getPixelColorAtWorld(wx: number, wy: number): {
148
+ r: number;
149
+ g: number;
150
+ b: number;
151
+ a: number;
152
+ rgba: string;
153
+ hex: string;
154
+ };
155
+ registerLayerManager(manager: LayerManagerBase): void;
156
+ unregisterLayerManager(manager: LayerManagerBase): void;
157
+ getLayerManagers(): LayerManagerBase[];
158
+ /**
159
+ * Smoothly reset to zoom=1, pan=(0,0).
160
+ */
161
+ resetSmooth(): void;
162
+ /**
163
+ * Instantly reset (no animation).
164
+ */
165
+ resetInstant(): void;
166
+ /**
167
+ * Convert screen (canvas client) -> world.
168
+ */
169
+ toWorld(x: number, y: number): {
170
+ wx: number;
171
+ wy: number;
172
+ };
173
+ /**
174
+ * Convert world -> screen (canvas client).
175
+ */
176
+ toScreen(wx: number, wy: number): {
177
+ x: number;
178
+ y: number;
179
+ };
180
+ /**
181
+ * Get current transform.
182
+ */
183
+ getTransform(): {
184
+ zoom: number;
185
+ tx: number;
186
+ ty: number;
187
+ };
188
+ /**
189
+ * Get viewport bounds in world coordinates.
190
+ */
191
+ getViewportBounds(): {
192
+ left: number;
193
+ top: number;
194
+ right: number;
195
+ bottom: number;
196
+ width: number;
197
+ height: number;
198
+ };
199
+ /**
200
+ * Set zoom range.
201
+ */
202
+ setZoomRange(minZoom: number, maxZoom: number): void;
203
+ /**
204
+ * Resize canvas to match parent size and DPR.
205
+ */
206
+ resizeToParent(): void;
207
+ /**
208
+ * Destroy and cleanup.
209
+ */
210
+ destroy(): void;
211
+ constructor(canvas: HTMLCanvasElement, render: RenderFn, options?: ViewManagerOption);
212
+ }
213
+ export { ViewManager };
214
+ export type { ViewManagerOption, RenderFn, UpdateCallback, RenderCallback };
@@ -0,0 +1,9 @@
1
+ export * from './core/view-manager';
2
+ export * from './layer/layer.base';
3
+ export * from './layer/layer.bitmap';
4
+ export * from './layer/layer.canvas';
5
+ export * from './layer/layer-manager.base';
6
+ export * from './layer/layer-manager.content';
7
+ export * from './layer/layer-manager.top-screen';
8
+ export * from './plugins';
9
+ export type * from './types/index';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";function t(t,e,i,s){return new(i||(i=Promise))(function(n,h){function a(t){try{r(s.next(t))}catch(t){h(t)}}function o(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i(function(t){t(e)})).then(a,o)}r((s=s.apply(t,e||[])).next())})}function e(t,e,i,s){if("a"===i&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?s:"a"===i?s.call(t):s?s.value:e.get(t)}function i(t,e,i,s,n){if("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(t,i):n?n.value=i:e.set(t,i),i}"function"==typeof SuppressedError&&SuppressedError;const s=(e,i)=>t(void 0,void 0,void 0,function*(){return new Promise((t,s)=>{const n=new Image;"string"==typeof e?(void 0!==i&&(n.crossOrigin=i),n.src=e):n.src=URL.createObjectURL(e),n.onload=()=>{t(n)},n.onerror=()=>{s(new Error("Image load failed"))}})}),n=(t,e,i)=>Math.min(Math.max(t,e),i);let h=0;class a{constructor(t,e,i="world"){this.space="world",this.visible=!0,this.opacity=1,this.blend="source-over",this.name=t,this.id=`layer_${e}_${++h}`,this.type=e,this.space=i}}class o extends a{beginStroke(t,e){const{lx:i,ly:s}=this.toLocalPoint(t,e);this._lastX=i,this._lastY=s,this._drawing=!0}stroke(t,e,i,s,n=1,h="brush"){if(!this._drawing)return;const{lx:a,ly:o}=this.toLocalPoint(t,e);this.context.beginPath(),this.context.moveTo(this._lastX,this._lastY),this.context.lineTo(a,o),"eraser"===h?(this.context.globalCompositeOperation="destination-out",this.context.strokeStyle="rgba(0, 0, 0, 1)"):(this.context.globalCompositeOperation="source-over",this.context.strokeStyle=i),this.context.lineWidth=s*n,this.context.lineCap="round",this.context.lineJoin="round",this.context.stroke(),this.context.closePath(),this._lastX=a,this._lastY=o}endStroke(){this._drawing=!1}isDrawing(){return this._drawing}captureSnapshot(t){try{if(t){const{x:e,y:i,width:s,height:n}=t,h=Math.max(0,Math.floor(e)),a=Math.max(0,Math.floor(i)),o=Math.min(this.canvas.width-h,Math.ceil(s)),r=Math.min(this.canvas.height-a,Math.ceil(n));return o<=0||r<=0?null:this.context.getImageData(h,a,o,r)}return this.context.getImageData(0,0,this.canvas.width,this.canvas.height)}catch(t){return null}}restoreSnapshot(t,e){var i,s;const n=null!==(i=null==e?void 0:e.x)&&void 0!==i?i:0,h=null!==(s=null==e?void 0:e.y)&&void 0!==s?s:0;this.context.putImageData(t,n,h)}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(){var t;null===(t=this._redraw)||void 0===t||t.call(this,this.context,this.canvas)}drawImage(t,e,i,s,n){this.context.drawImage(t,e,i,null!=s?s:t.width,null!=n?n:t.height)}hitTest(t,e){const{lx:i,ly:s}=this.toLocalPoint(t,e);return i>=0&&i<=this.canvas.width&&s>=0&&s<=this.canvas.height}toLocalPoint(t,e){const i=t-this.x,s=e-this.y,n=Math.cos(-this.rotation),h=Math.sin(-this.rotation),a=i*h+s*n,o=(i*n-s*h)/this.scale,r=a/this.scale;return{lx:o+("center"===this.anchor?this.canvas.width/2:0),ly:r+("center"===this.anchor?this.canvas.height/2:0)}}render(t,e){if(!this.visible)return;const i=this.canvas.width*this.scale,s=this.canvas.height*this.scale,n="center"===this.anchor?-i/2:0,h="center"===this.anchor?-s/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,h,i,s),t.restore()}cropTo(t){const e=Math.max(1,Math.floor(t.width)),i=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&i===this.canvas.height)return;const s=this._cloneCanvas(),n=Math.min(s.width,e),h=Math.min(s.height,i);this._setCanvasSize(e,i),n>0&&h>0&&this.context.drawImage(s,0,0,n,h,0,0,n,h)}resizeTo(t){const e=Math.max(1,Math.floor(t.width)),i=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&i===this.canvas.height)return;const s=this._cloneCanvas();this._setCanvasSize(e,i),s.width>0&&s.height>0&&this.context.drawImage(s,0,0,s.width,s.height,0,0,e,i)}_cloneCanvas(){const t=document.createElement("canvas");if(t.width=this.canvas.width,t.height=this.canvas.height,0===t.width||0===t.height)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){var e,i;super(t.name||"","canvas",null!==(e=t.space)&&void 0!==e?e:"world"),this.x=0,this.y=0,this.scale=1,this.rotation=0,this.anchor="topLeft",this._drawing=!1,this._lastX=0,this._lastY=0,this.canvas=document.createElement("canvas"),this.canvas.width=t.width,this.canvas.height=t.height;const s=this.canvas.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Offscreen 2D context unavailable");this.context=s,this.x=t.x||0,this.y=t.y||0,this.scale=null!==(i=t.scale)&&void 0!==i?i:1,this.rotation=t.rotation||0,t.anchor&&(this.anchor=t.anchor),this._redraw=t.redraw,this._redraw&&this._redraw(this.context,this.canvas)}}var r;class c extends o{static fromImage(e){return t(this,void 0,void 0,function*(){var t,n,h,a,o,_,l,d;const g=yield s(e.src,e.crossOrigin),m=null!==(t=e.width)&&void 0!==t?t:g.naturalWidth,u=null!==(n=e.height)&&void 0!==n?n:g.naturalHeight,p=new c({name:e.name,space:null!==(h=e.space)&&void 0!==h?h:"world",x:null!==(a=e.x)&&void 0!==a?a:0,y:null!==(o=e.y)&&void 0!==o?o:0,scale:null!==(_=e.scale)&&void 0!==_?_:1,rotation:null!==(l=e.rotation)&&void 0!==l?l:0,anchor:null!==(d=e.anchor)&&void 0!==d?d:"topLeft",width:m,height:u});return p.context.clearRect(0,0,p.canvas.width,p.canvas.height),p.context.drawImage(g,0,0,m,u),"string"!=typeof e.src&&i(p,r,g.src.startsWith("blob:")?g.src:null,"f"),p})}setSource(n,h){return t(this,void 0,void 0,function*(){const t=yield s(n,h);if(this.canvas.width=t.naturalWidth,this.canvas.height=t.naturalHeight,this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.drawImage(t,0,0),e(this,r,"f")){try{URL.revokeObjectURL(e(this,r,"f"))}catch(t){}i(this,r,null,"f")}"string"!=typeof n&&i(this,r,t.src.startsWith("blob:")?t.src:null,"f")})}paint(t){t(this.context,this.canvas)}getImageData(t=0,e=0,i=this.canvas.width,s=this.canvas.height){return this.context.getImageData(t,e,i,s)}putImageData(t,e=0,i=0){this.context.putImageData(t,e,i)}toDataURL(t="image/png",e){return this.canvas.toDataURL(t,e)}toImageBitmap(t){return createImageBitmap(this.canvas,null!=t?t:{})}destroy(){var t;if(null===(t=super.destroy)||void 0===t||t.call(this),e(this,r,"f")){try{URL.revokeObjectURL(e(this,r,"f"))}catch(t){}i(this,r,null,"f")}}constructor(t){var e,i;super({name:t.name,space:null!==(e=t.space)&&void 0!==e?e:"world",x:t.x,y:t.y,scale:t.scale,rotation:t.rotation,anchor:null!==(i=t.anchor)&&void 0!==i?i:"topLeft",width:t.width,height:t.height}),r.set(this,null),this.type="bitmap"}}r=new WeakMap;class _{constructor(){this._worldLayers=[],this._screenLayers=[]}_renderAllLayersIn(t,e){e.save(),t.applyWorldTransform(e);for(const i of this._worldLayers)!i.visible||i.opacity<=0||(e.save(),i.render(e,t),e.restore());e.restore(),e.save(),t.applyScreenTransform(e);for(const i of this._screenLayers)!i.visible||i.opacity<=0||(e.save(),i.render(e,t),e.restore());e.restore()}addLayer(t,e){const i="world"===t.space?this._worldLayers:this._screenLayers;return"number"==typeof e&&e>=0&&e<i.length?(i.splice(e,0,t),t.id):(i.push(t),t.id)}createImageLayer(e){return t(this,void 0,void 0,function*(){const t=yield c.fromImage(e);return this.addLayer(t),t})}createCanvasLayer(t){const e=new o(t);return this.addLayer(e),e}removeLayer(t){var e,i,s,n;const h=this._worldLayers.findIndex(e=>e.id===t);if(h>=0)return null===(i=(e=this._worldLayers[h]).destroy)||void 0===i||i.call(e),void this._worldLayers.splice(h,1);const a=this._screenLayers.findIndex(e=>e.id===t);a>=0&&(null===(n=(s=this._screenLayers[a]).destroy)||void 0===n||n.call(s),this._screenLayers.splice(a,1))}moveLayer(t,e){const i=this._worldLayers.findIndex(e=>e.id===t);if(i>=0){const[t]=this._worldLayers.splice(i,1),s=Math.max(0,Math.min(e,this._worldLayers.length));return void this._worldLayers.splice(s,0,t)}const s=this._screenLayers.findIndex(e=>e.id===t);if(s>=0){const[t]=this._screenLayers.splice(s,1),i=Math.max(0,Math.min(e,this._screenLayers.length));this._screenLayers.splice(i,0,t)}}getLayer(t){return this._worldLayers.find(e=>e.id===t)||this._screenLayers.find(e=>e.id===t)}getAllLayers(t){return t?("world"===t?this._worldLayers:this._screenLayers).slice():[...this._worldLayers,...this._screenLayers]}hitTest(t,e,i="world"){const s=this.getAllLayers(i);for(let i=s.length-1;i>=0;i--){const n=s[i];if(n.hitTest&&n.hitTest(t,e))return n}}destroy(){var t;for(const e of[...this._worldLayers,...this._screenLayers])null===(t=e.destroy)||void 0===t||t.call(e);this._worldLayers=[],this._screenLayers=[]}}class l{constructor(t){this.name="interaction",this._view=null,this._dragging=!1,this._vx=0,this._vy=0,this._lastMoveTs=0,this._activePointerId=null,this._onDownBound=t=>this._onPointerDown(t),this._onMoveBound=t=>this._onPointerMove(t),this._onUpBound=()=>this._onPointerUp(),this._onWheelBound=t=>this._onWheel(t),this._onUpdate=t=>{if(!this._view)return;const{friction:e,stopSpeed:i}=this._options,s=Math.hypot(this._vx,this._vy)>=i;if(!this._dragging&&this._panEnabled&&s){const s=this._vx*t,n=this._vy*t;this._view.panBy(s,n),this._vx*=e,this._vy*=e,Math.hypot(this._vx,this._vy)<i&&(this._vx=0,this._vy=0)}else this._panEnabled||(this._vx=0,this._vy=0)},this._options=Object.assign({panEnabled:!0,zoomEnabled:!0,friction:.92,stopSpeed:.02,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}_onPointerDown(t){var e;if(0===t.button&&this._panEnabled){this._dragging=!0,this._vx=0,this._vy=0,this._lastMoveTs=performance.now(),this._activePointerId=t.pointerId;try{null===(e=this._view)||void 0===e||e.canvas.setPointerCapture(t.pointerId)}catch(t){}}}_onPointerMove(t){if(!this._dragging||!this._panEnabled||!this._view)return;const e=performance.now(),i=Math.max(1,e-(this._lastMoveTs||e-16));this._lastMoveTs=e;const s=t.movementX,n=t.movementY;this._view.panBy(s,n);const h=this._options.emaAlpha,a=s/i,o=n/i;this._vx=(1-h)*this._vx+h*a,this._vy=(1-h)*this._vy+h*o}_onPointerUp(){if(!this._dragging)return;this._dragging=!1;const t=performance.now(),e=this._lastMoveTs?t-this._lastMoveTs:1/0;if(null!=this._activePointerId&&this._view){try{this._view.canvas.releasePointerCapture(this._activePointerId)}catch(t){}this._activePointerId=null}if(e>=this._options.idleNoInertiaMs)this._vx=0,this._vy=0;else{const t=Math.pow(this._options.friction,e/16);this._vx*=t,this._vy*=t}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||"normal"===t)return 16;const e=parseFloat(t);return Number.isFinite(e)?e:16}_normalizeWheelDelta(t){if(!this._view)return 0;let e=t.deltaY;if(1===t.deltaMode)e*=this._getLineHeightPx();else if(2===t.deltaMode){e*=this._view.canvas.clientHeight||window.innerHeight||800}return e}_onWheel(t){if(!this._zoomEnabled||!this._view)return;t.preventDefault(),t.stopPropagation();const e=this._normalizeWheelDelta(t),i=this._view.canvas.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;let h=-e*this._options.wheelSensitivity;t.ctrlKey||t.metaKey?h*=1.6:t.shiftKey&&(h*=.6),this._view.zoomByLogAtScreen(s,n,h)}}class d{constructor(t){var e,i,s,n;this.name="document",this._view=null,this._enabled=!1,this._x=0,this._y=0,this._width=0,this._height=0,this._marginL=0,this._marginR=0,this._marginT=0,this._marginB=0,this._panClampMode="minVisible",this._onUpdate=()=>{this._enabled&&this._view&&this._clampPan()},this._onBeforeRender=t=>{this._enabled&&this._view&&(t.save(),t.beginPath(),t.rect(this._x,this._y,this._width,this._height),t.clip())},this._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()}},this._options=Object.assign({rect:{x:0,y:0,width:0,height:0},margins:{},drawBorder:!1,minVisiblePx:30,panClampMode:"minVisible"},t),(null==t?void 0: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),(null==t?void 0:t.margins)&&(this._marginL=null!==(e=t.margins.left)&&void 0!==e?e:0,this._marginR=null!==(i=t.margins.right)&&void 0!==i?i:0,this._marginT=null!==(s=t.margins.top)&&void 0!==s?s:0,this._marginB=null!==(n=t.margins.bottom)&&void 0!==n?n: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,i,s){this._enabled=!0,this._x=t,this._y=e,this._width=i,this._height=s}clearRect(){this._enabled=!1}setMargins(t){var e,i,s,n;this._marginL=null!==(e=t.left)&&void 0!==e?e:this._marginL,this._marginR=null!==(i=t.right)&&void 0!==i?i:this._marginR,this._marginT=null!==(s=t.top)&&void 0!==s?s:this._marginT,this._marginB=null!==(n=t.bottom)&&void 0!==n?n: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,i=this._view.canvas.width/e,s=this._view.canvas.height/e,n=Math.max(1,i-(this._marginL+this._marginR)),h=Math.max(1,s-(this._marginT+this._marginB));let a;const o=n/this._width,r=h/this._height;a="contain"===t?Math.min(o,r):"cover"===t?Math.max(o,r):"fitWidth"===t?o:r,a=Math.min(this._view.maxZoom,Math.max(this._view.minZoom,a));const c=this._marginL+(n-a*this._width)/2,_=this._marginT+(h-a*this._height)/2,l=c-a*this._x,d=_-a*this._y;this._view.setTransform(a,l,d)}isPointInDocument(t,e){return!this._enabled||t>=this._x&&t<=this._x+this._width&&e>=this._y&&e<=this._y+this._height}_clampPan(){if(!this._view)return;const{zoom:t,tx:e,ty:i}=this._view.getTransform(),s=t,n=this._view.dpr,h=this._view.canvas.width/n,a=this._view.canvas.height/n,o=this._x,r=this._y,c=this._x+this._width,_=this._y+this._height;let l=e,d=i;if("margin"===this._panClampMode){const t=this._marginL-s*o,n=h-this._marginR-s*c,g=this._marginT-s*r,m=a-this._marginB-s*_,u=Math.max(1,h-(this._marginL+this._marginR)),p=Math.max(1,a-(this._marginT+this._marginB));l=s*this._width<=u?this._marginL+(u-s*this._width)/2-s*this._x:Math.min(t,Math.max(n,e)),d=s*this._height<=p?this._marginT+(p-s*this._height)/2-s*this._y:Math.min(g,Math.max(m,i))}else if("minVisible"===this._panClampMode){const t=s*this._width,n=s*this._height,g=Math.min(this._options.minVisiblePx,t),m=Math.min(this._options.minVisiblePx,n),u=h-g-s*o,p=g-s*c,v=a-m-s*r,w=m-s*_;l=p<=u?Math.min(u,Math.max(p,e)):(p+u)/2,d=w<=v?Math.min(v,Math.max(w,i)):(w+v)/2}l===e&&d===i||this._view.setPan(l,d)}_doResize(t,e){if(!this._view)return;const i=Math.max(1,Math.floor(e.width)),s=Math.max(1,Math.floor(e.height)),n={width:i,height:s},h=this._view.getLayerManagers();for(const e of h){const i=e.getAllLayers("world");for(const e of i){const i=e;"function"==typeof i.cropTo&&"function"==typeof i.resizeTo&&("crop"===t?i.cropTo(n):i.resizeTo(n))}}this._width=i,this._height=s,this._enabled&&this._clampPan()}}class g{constructor(t,e,i,s){this.type="snapshot",this._isExecuted=!0,this._target=t,this._beforeData=e,this._afterData=i,this._region=null==s?void 0:s.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}}exports.BitmapLayer=c,exports.CanvasLayer=o,exports.ContentLayerManager=class extends _{constructor(){super(...arguments),this._compositeCache=null,this._compositeCacheCtx=null,this._compositeDirty=!0,this._lastCacheWidth=0,this._lastCacheHeight=0,this._cachedBoundsMinX=0,this._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,i=this._worldLayers;0!==i.length&&(this._compositeDirty&&this._rebuildCompositeCache(i),this._compositeCache&&this._compositeCacheCtx&&e.drawImage(this._compositeCache,this._cachedBoundsMinX,this._cachedBoundsMinY))}_rebuildCompositeCache(t){let e=0,i=0,s=0,n=0,h=!1;for(const a of t){const t=a,o=t.canvas.width*t.scale,r=t.canvas.height*t.scale,c="center"===t.anchor?-o/2:0,_="center"===t.anchor?-r/2:0,l=Math.cos(t.rotation),d=Math.sin(t.rotation),g=[{x:c,y:_},{x:c+o,y:_},{x:c,y:_+r},{x:c+o,y:_+r}];let m=1/0,u=1/0,p=-1/0,v=-1/0;for(const e of g){const i=e.x*l-e.y*d+t.x,s=e.x*d+e.y*l+t.y;m=Math.min(m,i),u=Math.min(u,s),p=Math.max(p,i),v=Math.max(v,s)}h?(e=Math.min(e,m),i=Math.min(i,u),s=Math.max(s,p),n=Math.max(n,v)):(e=m,i=u,s=p,n=v,h=!0)}if(!h)return void(this._compositeDirty=!1);const a=Math.ceil(s-e),o=Math.ceil(n-i);this._compositeCache||(this._compositeCache=document.createElement("canvas"),this._compositeCacheCtx=this._compositeCache.getContext("2d",{alpha:!0})),this._lastCacheWidth===a&&this._lastCacheHeight===o||(this._compositeCache.width=a,this._compositeCache.height=o,this._lastCacheWidth=a,this._lastCacheHeight=o),this._cachedBoundsMinX=e,this._cachedBoundsMinY=i;const r=this._compositeCacheCtx;r.clearRect(0,0,a,o),r.save(),r.translate(-e,-i);for(const e of t)!e.visible||e.opacity<=0||(r.save(),e.render(r),r.restore());r.restore(),this._compositeDirty=!1}destroy(){super.destroy(),this._compositeCache=null,this._compositeCacheCtx=null}},exports.DocumentPlugin=d,exports.HistoryManager=class{executeCommand(t){t.execute(),this.addCommand(t)}addCommand(t){var e,i;this._redoStack=[];const s=this._undoStack[this._undoStack.length-1];if(s&&(null===(e=s.canMerge)||void 0===e?void 0:e.call(s,t))&&s.merge){const e=null!==(i=s.merge(t))&&void 0!==i?i:s;return void(e!==s&&(this._undoStack[this._undoStack.length-1]=e))}this._undoStack.push(t),this._undoStack.length>this._maxHistorySize&&this._undoStack.shift()}undo(){if(0===this._undoStack.length)return null;const t=this._undoStack.pop();return t.undo(),this._redoStack.push(t),t}redo(){if(0===this._redoStack.length)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){var e,i,s;this._undoStack=[],this._redoStack=[],this._maxHistorySize=null!==(e=null==t?void 0:t.maxHistorySize)&&void 0!==e?e:50,this._undoStack=null!==(i=null==t?void 0:t.undoStack)&&void 0!==i?i:[],this._redoStack=null!==(s=null==t?void 0:t.redoStack)&&void 0!==s?s:[]}},exports.InteractionPlugin=l,exports.LayerBase=a,exports.LayerManagerBase=_,exports.SnapshotCommand=g,exports.TopScreenLayerManager=class extends _{renderAllLayersIn(t){const e=t.topScreenContext;this._renderAllLayersIn(t,e)}},exports.ViewManager=class{get zoom(){return Math.exp(this._currentLogZ)}get minZoom(){return this._options.minZoom}get maxZoom(){return this._options.maxZoom}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 n(t,this.LOG_MIN,this.LOG_MAX)}_setTargetLogZoomAtScreen(t,e,i){Number.isFinite(i)&&(this._anchorX=t,this._anchorY=e,this._targetLogZ=this._clampLog(i),this._needsRender=!0)}zoomToAtScreen(t,e,i){this._setTargetLogZoomAtScreen(t,e,Math.log(i))}zoomToAtScreenRaw(t,e,i){if(!Number.isFinite(i))return;const s=Math.max(1e-8,this._options.minZoom),h=this._options.maxZoom,a=n(i,s,h),o=Math.exp(this._currentLogZ),r=a;if(!Number.isFinite(o)||o<=0)return;if(Math.abs(r-o)<1e-12)return;const c=Math.log(a);this._currentLogZ=c,this._targetLogZ=c;const _=r/o;this._tx=t-(t-this._tx)*_,this._ty=e-(e-this._ty)*_,this._needsRender=!0}zoomToAtWorld(t,e,i){const{x:s,y:n}=this.toScreen(t,e);this.zoomToAtScreen(s,n,i)}zoomByFactorAtScreen(t,e,i){if(i<=0||!Number.isFinite(i))return;const s=Math.log(i);this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+s)}zoomByLogAtScreen(t,e,i){this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+i)}zoomByFactorAtWorld(t,e,i){const{x:s,y:n}=this.toScreen(t,e);this.zoomByFactorAtScreen(s,n,i)}zoomInAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,i=t.height/2;this.zoomByFactorAtScreen(e,i,1.2)}zoomOutAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,i=t.height/2;this.zoomByFactorAtScreen(e,i,1/1.2)}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,i){const s=n(t,this._options.minZoom,this._options.maxZoom);this._currentLogZ=Math.log(s),this._targetLogZ=this._currentLogZ,this._tx=e,this._ty=i,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)return void(this._raf=requestAnimationFrame(()=>this._loop()));const t=performance.now(),e=Math.max(1,t-this._lastFrameTs);this._lastFrameTs=t;const{approachKZoom:i}=this._options,s=Math.exp(this._currentLogZ),n=this._targetLogZ-this._currentLogZ,h=Math.abs(n)>1e-6;if(h){const t=1-Math.exp(-i*e);this._currentLogZ+=n*t}const a=Math.exp(this._currentLogZ);if(a!==s){const t=this._anchorX,e=this._anchorY,i=a/s;this._tx=t-(t-this._tx)*i,this._ty=e-(e-this._ty)*i}if(this._isResetting){const t=1-Math.exp(-this._options.approachKPan*e);this._tx+=(0-this._tx)*t,this._ty+=(0-this._ty)*t;const i=Math.abs(this._currentLogZ)<.001&&Math.abs(this._targetLogZ)<1e-6,s=Math.abs(this._tx)<.5&&Math.abs(this._ty)<.5;i&&s&&(this._currentLogZ=0,this._targetLogZ=0,this._tx=0,this._ty=0,this._isResetting=!1)}for(const t of this._updateCallbacks)t(e);if(!(this._needsRender||h||this._isResetting))return void(this._raf=requestAnimationFrame(()=>this._loop()));this._needsRender=!1;const o=this.contentCanvas,r=this.contentContext,c=this.topScreenCanvas,_=this.topScreenContext,l=this.canvas,d=this.context;this._ensureOffscreenSizeLike(o,l),this._ensureOffscreenSizeLike(c,l),r.setTransform(1,0,0,1,0,0),_.setTransform(1,0,0,1,0,0),d.setTransform(1,0,0,1,0,0);const g=this._options.background;"string"==typeof g&&""!==g.trim()&&"transparent"!==g.toLowerCase()?(d.fillStyle=g,d.fillRect(0,0,l.width,l.height)):d.clearRect(0,0,l.width,l.height),r.clearRect(0,0,o.width,o.height),_.clearRect(0,0,c.width,c.height),r.setTransform(this._dpr*a,0,0,this._dpr*a,this._dpr*this._tx,this._dpr*this._ty);for(const t of this._beforeRenderCallbacks)t(r);this._render(this);for(const t of this._afterRenderCallbacks)t(r);d.drawImage(o,0,0),d.drawImage(c,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 i=Math.floor(t*this._dpr),s=Math.floor(e*this._dpr);if(i<0||s<0||i>=this.canvas.width||s>=this.canvas.height)return{r:0,g:0,b:0,a:0,rgba:"rgba(0,0,0,0)",hex:"#000000"};const n=this.contentContext.getImageData(i,s,1,1).data,h=n[0],a=n[1],o=n[2],r=n[3]/255,c=t=>t.toString(16).padStart(2,"0"),_=`#${c(h)}${c(a)}${c(o)}`;return{r:h,g:a,b:o,a:r,rgba:`rgba(${h},${a},${o},${r.toFixed(3)})`,hex:_}}getPixelColorAtWorld(t,e){const{x:i,y:s}=this.toScreen(t,e);return this.getPixelColorAtScreen(i,s)}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 i=Math.exp(this._currentLogZ);return{wx:(t-this._tx)/i,wy:(e-this._ty)/i}}toScreen(t,e){const i=Math.exp(this._currentLogZ);return{x:t*i+this._tx,y:e*i+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,i=this.canvas.height/this._dpr,s=-this._tx/t,n=-this._ty/t,h=(e-this._tx)/t,a=(i-this._ty)/t;return{left:s,top:n,right:h,bottom:a,width:h-s,height:a-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 t=(this.canvas.parentElement||this.canvas).getBoundingClientRect();this._dpr=Math.max(1,window.devicePixelRatio||1);const e=Math.max(1,Math.round(t.width)),i=Math.max(1,Math.round(t.height));this.canvas.width=Math.round(e*this._dpr),this.canvas.height=Math.round(i*this._dpr),this.canvas.style.width=`${e}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,i){this._layerManagers=[],this._plugins=new Map,this._isResetting=!1,this._isResizing=!1,this._needsRender=!0,this._raf=0,this._lastFrameTs=performance.now(),this._tx=0,this._ty=0,this._anchorX=0,this._anchorY=0,this._currentLogZ=Math.log(1),this._targetLogZ=Math.log(1),this._updateCallbacks=new Set,this._beforeRenderCallbacks=new Set,this._afterRenderCallbacks=new Set,this._dpr=Math.max(1,window.devicePixelRatio||1);const s=t.getContext("2d",{willReadFrequently:!0,alpha:!0});if(!s)throw new Error("2D context not available");this.canvas=t,this.context=s,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=Object.assign({minZoom:.5,maxZoom:10,approachKZoom:.022,approachKPan:.022,autoResize:!0,background:"#fff"},i),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())}},exports.createDocumentPlugin=function(t){return new d(t)},exports.createInteractionPlugin=function(t){return new l(t)},exports.createSnapshotCommand=function(t,e,i,s){return e&&i?new g(t,e,i,s):null};
@@ -0,0 +1 @@
1
+ function t(t,e,i,s){return new(i||(i=Promise))(function(n,h){function a(t){try{r(s.next(t))}catch(t){h(t)}}function o(t){try{r(s.throw(t))}catch(t){h(t)}}function r(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i(function(t){t(e)})).then(a,o)}r((s=s.apply(t,e||[])).next())})}function e(t,e,i,s){if("a"===i&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?s:"a"===i?s.call(t):s?s.value:e.get(t)}function i(t,e,i,s,n){if("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?n.call(t,i):n?n.value=i:e.set(t,i),i}"function"==typeof SuppressedError&&SuppressedError;const s=(e,i)=>t(void 0,void 0,void 0,function*(){return new Promise((t,s)=>{const n=new Image;"string"==typeof e?(void 0!==i&&(n.crossOrigin=i),n.src=e):n.src=URL.createObjectURL(e),n.onload=()=>{t(n)},n.onerror=()=>{s(new Error("Image load failed"))}})}),n=(t,e,i)=>Math.min(Math.max(t,e),i);class h{get zoom(){return Math.exp(this._currentLogZ)}get minZoom(){return this._options.minZoom}get maxZoom(){return this._options.maxZoom}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 n(t,this.LOG_MIN,this.LOG_MAX)}_setTargetLogZoomAtScreen(t,e,i){Number.isFinite(i)&&(this._anchorX=t,this._anchorY=e,this._targetLogZ=this._clampLog(i),this._needsRender=!0)}zoomToAtScreen(t,e,i){this._setTargetLogZoomAtScreen(t,e,Math.log(i))}zoomToAtScreenRaw(t,e,i){if(!Number.isFinite(i))return;const s=Math.max(1e-8,this._options.minZoom),h=this._options.maxZoom,a=n(i,s,h),o=Math.exp(this._currentLogZ),r=a;if(!Number.isFinite(o)||o<=0)return;if(Math.abs(r-o)<1e-12)return;const c=Math.log(a);this._currentLogZ=c,this._targetLogZ=c;const _=r/o;this._tx=t-(t-this._tx)*_,this._ty=e-(e-this._ty)*_,this._needsRender=!0}zoomToAtWorld(t,e,i){const{x:s,y:n}=this.toScreen(t,e);this.zoomToAtScreen(s,n,i)}zoomByFactorAtScreen(t,e,i){if(i<=0||!Number.isFinite(i))return;const s=Math.log(i);this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+s)}zoomByLogAtScreen(t,e,i){this._setTargetLogZoomAtScreen(t,e,this._targetLogZ+i)}zoomByFactorAtWorld(t,e,i){const{x:s,y:n}=this.toScreen(t,e);this.zoomByFactorAtScreen(s,n,i)}zoomInAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,i=t.height/2;this.zoomByFactorAtScreen(e,i,1.2)}zoomOutAtCenter(){const t=this.canvas.getBoundingClientRect(),e=t.width/2,i=t.height/2;this.zoomByFactorAtScreen(e,i,1/1.2)}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,i){const s=n(t,this._options.minZoom,this._options.maxZoom);this._currentLogZ=Math.log(s),this._targetLogZ=this._currentLogZ,this._tx=e,this._ty=i,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)return void(this._raf=requestAnimationFrame(()=>this._loop()));const t=performance.now(),e=Math.max(1,t-this._lastFrameTs);this._lastFrameTs=t;const{approachKZoom:i}=this._options,s=Math.exp(this._currentLogZ),n=this._targetLogZ-this._currentLogZ,h=Math.abs(n)>1e-6;if(h){const t=1-Math.exp(-i*e);this._currentLogZ+=n*t}const a=Math.exp(this._currentLogZ);if(a!==s){const t=this._anchorX,e=this._anchorY,i=a/s;this._tx=t-(t-this._tx)*i,this._ty=e-(e-this._ty)*i}if(this._isResetting){const t=1-Math.exp(-this._options.approachKPan*e);this._tx+=(0-this._tx)*t,this._ty+=(0-this._ty)*t;const i=Math.abs(this._currentLogZ)<.001&&Math.abs(this._targetLogZ)<1e-6,s=Math.abs(this._tx)<.5&&Math.abs(this._ty)<.5;i&&s&&(this._currentLogZ=0,this._targetLogZ=0,this._tx=0,this._ty=0,this._isResetting=!1)}for(const t of this._updateCallbacks)t(e);if(!(this._needsRender||h||this._isResetting))return void(this._raf=requestAnimationFrame(()=>this._loop()));this._needsRender=!1;const o=this.contentCanvas,r=this.contentContext,c=this.topScreenCanvas,_=this.topScreenContext,l=this.canvas,d=this.context;this._ensureOffscreenSizeLike(o,l),this._ensureOffscreenSizeLike(c,l),r.setTransform(1,0,0,1,0,0),_.setTransform(1,0,0,1,0,0),d.setTransform(1,0,0,1,0,0);const g=this._options.background;"string"==typeof g&&""!==g.trim()&&"transparent"!==g.toLowerCase()?(d.fillStyle=g,d.fillRect(0,0,l.width,l.height)):d.clearRect(0,0,l.width,l.height),r.clearRect(0,0,o.width,o.height),_.clearRect(0,0,c.width,c.height),r.setTransform(this._dpr*a,0,0,this._dpr*a,this._dpr*this._tx,this._dpr*this._ty);for(const t of this._beforeRenderCallbacks)t(r);this._render(this);for(const t of this._afterRenderCallbacks)t(r);d.drawImage(o,0,0),d.drawImage(c,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 i=Math.floor(t*this._dpr),s=Math.floor(e*this._dpr);if(i<0||s<0||i>=this.canvas.width||s>=this.canvas.height)return{r:0,g:0,b:0,a:0,rgba:"rgba(0,0,0,0)",hex:"#000000"};const n=this.contentContext.getImageData(i,s,1,1).data,h=n[0],a=n[1],o=n[2],r=n[3]/255,c=t=>t.toString(16).padStart(2,"0"),_=`#${c(h)}${c(a)}${c(o)}`;return{r:h,g:a,b:o,a:r,rgba:`rgba(${h},${a},${o},${r.toFixed(3)})`,hex:_}}getPixelColorAtWorld(t,e){const{x:i,y:s}=this.toScreen(t,e);return this.getPixelColorAtScreen(i,s)}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 i=Math.exp(this._currentLogZ);return{wx:(t-this._tx)/i,wy:(e-this._ty)/i}}toScreen(t,e){const i=Math.exp(this._currentLogZ);return{x:t*i+this._tx,y:e*i+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,i=this.canvas.height/this._dpr,s=-this._tx/t,n=-this._ty/t,h=(e-this._tx)/t,a=(i-this._ty)/t;return{left:s,top:n,right:h,bottom:a,width:h-s,height:a-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 t=(this.canvas.parentElement||this.canvas).getBoundingClientRect();this._dpr=Math.max(1,window.devicePixelRatio||1);const e=Math.max(1,Math.round(t.width)),i=Math.max(1,Math.round(t.height));this.canvas.width=Math.round(e*this._dpr),this.canvas.height=Math.round(i*this._dpr),this.canvas.style.width=`${e}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,i){this._layerManagers=[],this._plugins=new Map,this._isResetting=!1,this._isResizing=!1,this._needsRender=!0,this._raf=0,this._lastFrameTs=performance.now(),this._tx=0,this._ty=0,this._anchorX=0,this._anchorY=0,this._currentLogZ=Math.log(1),this._targetLogZ=Math.log(1),this._updateCallbacks=new Set,this._beforeRenderCallbacks=new Set,this._afterRenderCallbacks=new Set,this._dpr=Math.max(1,window.devicePixelRatio||1);const s=t.getContext("2d",{willReadFrequently:!0,alpha:!0});if(!s)throw new Error("2D context not available");this.canvas=t,this.context=s,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=Object.assign({minZoom:.5,maxZoom:10,approachKZoom:.022,approachKPan:.022,autoResize:!0,background:"#fff"},i),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 a=0;class o{constructor(t,e,i="world"){this.space="world",this.visible=!0,this.opacity=1,this.blend="source-over",this.name=t,this.id=`layer_${e}_${++a}`,this.type=e,this.space=i}}class r extends o{beginStroke(t,e){const{lx:i,ly:s}=this.toLocalPoint(t,e);this._lastX=i,this._lastY=s,this._drawing=!0}stroke(t,e,i,s,n=1,h="brush"){if(!this._drawing)return;const{lx:a,ly:o}=this.toLocalPoint(t,e);this.context.beginPath(),this.context.moveTo(this._lastX,this._lastY),this.context.lineTo(a,o),"eraser"===h?(this.context.globalCompositeOperation="destination-out",this.context.strokeStyle="rgba(0, 0, 0, 1)"):(this.context.globalCompositeOperation="source-over",this.context.strokeStyle=i),this.context.lineWidth=s*n,this.context.lineCap="round",this.context.lineJoin="round",this.context.stroke(),this.context.closePath(),this._lastX=a,this._lastY=o}endStroke(){this._drawing=!1}isDrawing(){return this._drawing}captureSnapshot(t){try{if(t){const{x:e,y:i,width:s,height:n}=t,h=Math.max(0,Math.floor(e)),a=Math.max(0,Math.floor(i)),o=Math.min(this.canvas.width-h,Math.ceil(s)),r=Math.min(this.canvas.height-a,Math.ceil(n));return o<=0||r<=0?null:this.context.getImageData(h,a,o,r)}return this.context.getImageData(0,0,this.canvas.width,this.canvas.height)}catch(t){return null}}restoreSnapshot(t,e){var i,s;const n=null!==(i=null==e?void 0:e.x)&&void 0!==i?i:0,h=null!==(s=null==e?void 0:e.y)&&void 0!==s?s:0;this.context.putImageData(t,n,h)}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(){var t;null===(t=this._redraw)||void 0===t||t.call(this,this.context,this.canvas)}drawImage(t,e,i,s,n){this.context.drawImage(t,e,i,null!=s?s:t.width,null!=n?n:t.height)}hitTest(t,e){const{lx:i,ly:s}=this.toLocalPoint(t,e);return i>=0&&i<=this.canvas.width&&s>=0&&s<=this.canvas.height}toLocalPoint(t,e){const i=t-this.x,s=e-this.y,n=Math.cos(-this.rotation),h=Math.sin(-this.rotation),a=i*h+s*n,o=(i*n-s*h)/this.scale,r=a/this.scale;return{lx:o+("center"===this.anchor?this.canvas.width/2:0),ly:r+("center"===this.anchor?this.canvas.height/2:0)}}render(t,e){if(!this.visible)return;const i=this.canvas.width*this.scale,s=this.canvas.height*this.scale,n="center"===this.anchor?-i/2:0,h="center"===this.anchor?-s/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,h,i,s),t.restore()}cropTo(t){const e=Math.max(1,Math.floor(t.width)),i=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&i===this.canvas.height)return;const s=this._cloneCanvas(),n=Math.min(s.width,e),h=Math.min(s.height,i);this._setCanvasSize(e,i),n>0&&h>0&&this.context.drawImage(s,0,0,n,h,0,0,n,h)}resizeTo(t){const e=Math.max(1,Math.floor(t.width)),i=Math.max(1,Math.floor(t.height));if(e===this.canvas.width&&i===this.canvas.height)return;const s=this._cloneCanvas();this._setCanvasSize(e,i),s.width>0&&s.height>0&&this.context.drawImage(s,0,0,s.width,s.height,0,0,e,i)}_cloneCanvas(){const t=document.createElement("canvas");if(t.width=this.canvas.width,t.height=this.canvas.height,0===t.width||0===t.height)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){var e,i;super(t.name||"","canvas",null!==(e=t.space)&&void 0!==e?e:"world"),this.x=0,this.y=0,this.scale=1,this.rotation=0,this.anchor="topLeft",this._drawing=!1,this._lastX=0,this._lastY=0,this.canvas=document.createElement("canvas"),this.canvas.width=t.width,this.canvas.height=t.height;const s=this.canvas.getContext("2d",{willReadFrequently:!0});if(!s)throw new Error("Offscreen 2D context unavailable");this.context=s,this.x=t.x||0,this.y=t.y||0,this.scale=null!==(i=t.scale)&&void 0!==i?i:1,this.rotation=t.rotation||0,t.anchor&&(this.anchor=t.anchor),this._redraw=t.redraw,this._redraw&&this._redraw(this.context,this.canvas)}}var c;class _ extends r{static fromImage(e){return t(this,void 0,void 0,function*(){var t,n,h,a,o,r,l,d;const g=yield s(e.src,e.crossOrigin),m=null!==(t=e.width)&&void 0!==t?t:g.naturalWidth,u=null!==(n=e.height)&&void 0!==n?n:g.naturalHeight,p=new _({name:e.name,space:null!==(h=e.space)&&void 0!==h?h:"world",x:null!==(a=e.x)&&void 0!==a?a:0,y:null!==(o=e.y)&&void 0!==o?o:0,scale:null!==(r=e.scale)&&void 0!==r?r:1,rotation:null!==(l=e.rotation)&&void 0!==l?l:0,anchor:null!==(d=e.anchor)&&void 0!==d?d:"topLeft",width:m,height:u});return p.context.clearRect(0,0,p.canvas.width,p.canvas.height),p.context.drawImage(g,0,0,m,u),"string"!=typeof e.src&&i(p,c,g.src.startsWith("blob:")?g.src:null,"f"),p})}setSource(n,h){return t(this,void 0,void 0,function*(){const t=yield s(n,h);if(this.canvas.width=t.naturalWidth,this.canvas.height=t.naturalHeight,this.context.setTransform(1,0,0,1,0,0),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.drawImage(t,0,0),e(this,c,"f")){try{URL.revokeObjectURL(e(this,c,"f"))}catch(t){}i(this,c,null,"f")}"string"!=typeof n&&i(this,c,t.src.startsWith("blob:")?t.src:null,"f")})}paint(t){t(this.context,this.canvas)}getImageData(t=0,e=0,i=this.canvas.width,s=this.canvas.height){return this.context.getImageData(t,e,i,s)}putImageData(t,e=0,i=0){this.context.putImageData(t,e,i)}toDataURL(t="image/png",e){return this.canvas.toDataURL(t,e)}toImageBitmap(t){return createImageBitmap(this.canvas,null!=t?t:{})}destroy(){var t;if(null===(t=super.destroy)||void 0===t||t.call(this),e(this,c,"f")){try{URL.revokeObjectURL(e(this,c,"f"))}catch(t){}i(this,c,null,"f")}}constructor(t){var e,i;super({name:t.name,space:null!==(e=t.space)&&void 0!==e?e:"world",x:t.x,y:t.y,scale:t.scale,rotation:t.rotation,anchor:null!==(i=t.anchor)&&void 0!==i?i:"topLeft",width:t.width,height:t.height}),c.set(this,null),this.type="bitmap"}}c=new WeakMap;class l{constructor(){this._worldLayers=[],this._screenLayers=[]}_renderAllLayersIn(t,e){e.save(),t.applyWorldTransform(e);for(const i of this._worldLayers)!i.visible||i.opacity<=0||(e.save(),i.render(e,t),e.restore());e.restore(),e.save(),t.applyScreenTransform(e);for(const i of this._screenLayers)!i.visible||i.opacity<=0||(e.save(),i.render(e,t),e.restore());e.restore()}addLayer(t,e){const i="world"===t.space?this._worldLayers:this._screenLayers;return"number"==typeof e&&e>=0&&e<i.length?(i.splice(e,0,t),t.id):(i.push(t),t.id)}createImageLayer(e){return t(this,void 0,void 0,function*(){const t=yield _.fromImage(e);return this.addLayer(t),t})}createCanvasLayer(t){const e=new r(t);return this.addLayer(e),e}removeLayer(t){var e,i,s,n;const h=this._worldLayers.findIndex(e=>e.id===t);if(h>=0)return null===(i=(e=this._worldLayers[h]).destroy)||void 0===i||i.call(e),void this._worldLayers.splice(h,1);const a=this._screenLayers.findIndex(e=>e.id===t);a>=0&&(null===(n=(s=this._screenLayers[a]).destroy)||void 0===n||n.call(s),this._screenLayers.splice(a,1))}moveLayer(t,e){const i=this._worldLayers.findIndex(e=>e.id===t);if(i>=0){const[t]=this._worldLayers.splice(i,1),s=Math.max(0,Math.min(e,this._worldLayers.length));return void this._worldLayers.splice(s,0,t)}const s=this._screenLayers.findIndex(e=>e.id===t);if(s>=0){const[t]=this._screenLayers.splice(s,1),i=Math.max(0,Math.min(e,this._screenLayers.length));this._screenLayers.splice(i,0,t)}}getLayer(t){return this._worldLayers.find(e=>e.id===t)||this._screenLayers.find(e=>e.id===t)}getAllLayers(t){return t?("world"===t?this._worldLayers:this._screenLayers).slice():[...this._worldLayers,...this._screenLayers]}hitTest(t,e,i="world"){const s=this.getAllLayers(i);for(let i=s.length-1;i>=0;i--){const n=s[i];if(n.hitTest&&n.hitTest(t,e))return n}}destroy(){var t;for(const e of[...this._worldLayers,...this._screenLayers])null===(t=e.destroy)||void 0===t||t.call(e);this._worldLayers=[],this._screenLayers=[]}}class d extends l{constructor(){super(...arguments),this._compositeCache=null,this._compositeCacheCtx=null,this._compositeDirty=!0,this._lastCacheWidth=0,this._lastCacheHeight=0,this._cachedBoundsMinX=0,this._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,i=this._worldLayers;0!==i.length&&(this._compositeDirty&&this._rebuildCompositeCache(i),this._compositeCache&&this._compositeCacheCtx&&e.drawImage(this._compositeCache,this._cachedBoundsMinX,this._cachedBoundsMinY))}_rebuildCompositeCache(t){let e=0,i=0,s=0,n=0,h=!1;for(const a of t){const t=a,o=t.canvas.width*t.scale,r=t.canvas.height*t.scale,c="center"===t.anchor?-o/2:0,_="center"===t.anchor?-r/2:0,l=Math.cos(t.rotation),d=Math.sin(t.rotation),g=[{x:c,y:_},{x:c+o,y:_},{x:c,y:_+r},{x:c+o,y:_+r}];let m=1/0,u=1/0,p=-1/0,v=-1/0;for(const e of g){const i=e.x*l-e.y*d+t.x,s=e.x*d+e.y*l+t.y;m=Math.min(m,i),u=Math.min(u,s),p=Math.max(p,i),v=Math.max(v,s)}h?(e=Math.min(e,m),i=Math.min(i,u),s=Math.max(s,p),n=Math.max(n,v)):(e=m,i=u,s=p,n=v,h=!0)}if(!h)return void(this._compositeDirty=!1);const a=Math.ceil(s-e),o=Math.ceil(n-i);this._compositeCache||(this._compositeCache=document.createElement("canvas"),this._compositeCacheCtx=this._compositeCache.getContext("2d",{alpha:!0})),this._lastCacheWidth===a&&this._lastCacheHeight===o||(this._compositeCache.width=a,this._compositeCache.height=o,this._lastCacheWidth=a,this._lastCacheHeight=o),this._cachedBoundsMinX=e,this._cachedBoundsMinY=i;const r=this._compositeCacheCtx;r.clearRect(0,0,a,o),r.save(),r.translate(-e,-i);for(const e of t)!e.visible||e.opacity<=0||(r.save(),e.render(r),r.restore());r.restore(),this._compositeDirty=!1}destroy(){super.destroy(),this._compositeCache=null,this._compositeCacheCtx=null}}class g extends l{renderAllLayersIn(t){const e=t.topScreenContext;this._renderAllLayersIn(t,e)}}class m{constructor(t){this.name="interaction",this._view=null,this._dragging=!1,this._vx=0,this._vy=0,this._lastMoveTs=0,this._activePointerId=null,this._onDownBound=t=>this._onPointerDown(t),this._onMoveBound=t=>this._onPointerMove(t),this._onUpBound=()=>this._onPointerUp(),this._onWheelBound=t=>this._onWheel(t),this._onUpdate=t=>{if(!this._view)return;const{friction:e,stopSpeed:i}=this._options,s=Math.hypot(this._vx,this._vy)>=i;if(!this._dragging&&this._panEnabled&&s){const s=this._vx*t,n=this._vy*t;this._view.panBy(s,n),this._vx*=e,this._vy*=e,Math.hypot(this._vx,this._vy)<i&&(this._vx=0,this._vy=0)}else this._panEnabled||(this._vx=0,this._vy=0)},this._options=Object.assign({panEnabled:!0,zoomEnabled:!0,friction:.92,stopSpeed:.02,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}_onPointerDown(t){var e;if(0===t.button&&this._panEnabled){this._dragging=!0,this._vx=0,this._vy=0,this._lastMoveTs=performance.now(),this._activePointerId=t.pointerId;try{null===(e=this._view)||void 0===e||e.canvas.setPointerCapture(t.pointerId)}catch(t){}}}_onPointerMove(t){if(!this._dragging||!this._panEnabled||!this._view)return;const e=performance.now(),i=Math.max(1,e-(this._lastMoveTs||e-16));this._lastMoveTs=e;const s=t.movementX,n=t.movementY;this._view.panBy(s,n);const h=this._options.emaAlpha,a=s/i,o=n/i;this._vx=(1-h)*this._vx+h*a,this._vy=(1-h)*this._vy+h*o}_onPointerUp(){if(!this._dragging)return;this._dragging=!1;const t=performance.now(),e=this._lastMoveTs?t-this._lastMoveTs:1/0;if(null!=this._activePointerId&&this._view){try{this._view.canvas.releasePointerCapture(this._activePointerId)}catch(t){}this._activePointerId=null}if(e>=this._options.idleNoInertiaMs)this._vx=0,this._vy=0;else{const t=Math.pow(this._options.friction,e/16);this._vx*=t,this._vy*=t}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||"normal"===t)return 16;const e=parseFloat(t);return Number.isFinite(e)?e:16}_normalizeWheelDelta(t){if(!this._view)return 0;let e=t.deltaY;if(1===t.deltaMode)e*=this._getLineHeightPx();else if(2===t.deltaMode){e*=this._view.canvas.clientHeight||window.innerHeight||800}return e}_onWheel(t){if(!this._zoomEnabled||!this._view)return;t.preventDefault(),t.stopPropagation();const e=this._normalizeWheelDelta(t),i=this._view.canvas.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;let h=-e*this._options.wheelSensitivity;t.ctrlKey||t.metaKey?h*=1.6:t.shiftKey&&(h*=.6),this._view.zoomByLogAtScreen(s,n,h)}}function u(t){return new m(t)}class p{constructor(t){var e,i,s,n;this.name="document",this._view=null,this._enabled=!1,this._x=0,this._y=0,this._width=0,this._height=0,this._marginL=0,this._marginR=0,this._marginT=0,this._marginB=0,this._panClampMode="minVisible",this._onUpdate=()=>{this._enabled&&this._view&&this._clampPan()},this._onBeforeRender=t=>{this._enabled&&this._view&&(t.save(),t.beginPath(),t.rect(this._x,this._y,this._width,this._height),t.clip())},this._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()}},this._options=Object.assign({rect:{x:0,y:0,width:0,height:0},margins:{},drawBorder:!1,minVisiblePx:30,panClampMode:"minVisible"},t),(null==t?void 0: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),(null==t?void 0:t.margins)&&(this._marginL=null!==(e=t.margins.left)&&void 0!==e?e:0,this._marginR=null!==(i=t.margins.right)&&void 0!==i?i:0,this._marginT=null!==(s=t.margins.top)&&void 0!==s?s:0,this._marginB=null!==(n=t.margins.bottom)&&void 0!==n?n: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,i,s){this._enabled=!0,this._x=t,this._y=e,this._width=i,this._height=s}clearRect(){this._enabled=!1}setMargins(t){var e,i,s,n;this._marginL=null!==(e=t.left)&&void 0!==e?e:this._marginL,this._marginR=null!==(i=t.right)&&void 0!==i?i:this._marginR,this._marginT=null!==(s=t.top)&&void 0!==s?s:this._marginT,this._marginB=null!==(n=t.bottom)&&void 0!==n?n: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,i=this._view.canvas.width/e,s=this._view.canvas.height/e,n=Math.max(1,i-(this._marginL+this._marginR)),h=Math.max(1,s-(this._marginT+this._marginB));let a;const o=n/this._width,r=h/this._height;a="contain"===t?Math.min(o,r):"cover"===t?Math.max(o,r):"fitWidth"===t?o:r,a=Math.min(this._view.maxZoom,Math.max(this._view.minZoom,a));const c=this._marginL+(n-a*this._width)/2,_=this._marginT+(h-a*this._height)/2,l=c-a*this._x,d=_-a*this._y;this._view.setTransform(a,l,d)}isPointInDocument(t,e){return!this._enabled||t>=this._x&&t<=this._x+this._width&&e>=this._y&&e<=this._y+this._height}_clampPan(){if(!this._view)return;const{zoom:t,tx:e,ty:i}=this._view.getTransform(),s=t,n=this._view.dpr,h=this._view.canvas.width/n,a=this._view.canvas.height/n,o=this._x,r=this._y,c=this._x+this._width,_=this._y+this._height;let l=e,d=i;if("margin"===this._panClampMode){const t=this._marginL-s*o,n=h-this._marginR-s*c,g=this._marginT-s*r,m=a-this._marginB-s*_,u=Math.max(1,h-(this._marginL+this._marginR)),p=Math.max(1,a-(this._marginT+this._marginB));l=s*this._width<=u?this._marginL+(u-s*this._width)/2-s*this._x:Math.min(t,Math.max(n,e)),d=s*this._height<=p?this._marginT+(p-s*this._height)/2-s*this._y:Math.min(g,Math.max(m,i))}else if("minVisible"===this._panClampMode){const t=s*this._width,n=s*this._height,g=Math.min(this._options.minVisiblePx,t),m=Math.min(this._options.minVisiblePx,n),u=h-g-s*o,p=g-s*c,v=a-m-s*r,w=m-s*_;l=p<=u?Math.min(u,Math.max(p,e)):(p+u)/2,d=w<=v?Math.min(v,Math.max(w,i)):(w+v)/2}l===e&&d===i||this._view.setPan(l,d)}_doResize(t,e){if(!this._view)return;const i=Math.max(1,Math.floor(e.width)),s=Math.max(1,Math.floor(e.height)),n={width:i,height:s},h=this._view.getLayerManagers();for(const e of h){const i=e.getAllLayers("world");for(const e of i){const i=e;"function"==typeof i.cropTo&&"function"==typeof i.resizeTo&&("crop"===t?i.cropTo(n):i.resizeTo(n))}}this._width=i,this._height=s,this._enabled&&this._clampPan()}}function v(t){return new p(t)}class w{constructor(t,e,i,s){this.type="snapshot",this._isExecuted=!0,this._target=t,this._beforeData=e,this._afterData=i,this._region=null==s?void 0:s.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 f(t,e,i,s){return e&&i?new w(t,e,i,s):null}class y{executeCommand(t){t.execute(),this.addCommand(t)}addCommand(t){var e,i;this._redoStack=[];const s=this._undoStack[this._undoStack.length-1];if(s&&(null===(e=s.canMerge)||void 0===e?void 0:e.call(s,t))&&s.merge){const e=null!==(i=s.merge(t))&&void 0!==i?i:s;return void(e!==s&&(this._undoStack[this._undoStack.length-1]=e))}this._undoStack.push(t),this._undoStack.length>this._maxHistorySize&&this._undoStack.shift()}undo(){if(0===this._undoStack.length)return null;const t=this._undoStack.pop();return t.undo(),this._redoStack.push(t),t}redo(){if(0===this._redoStack.length)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){var e,i,s;this._undoStack=[],this._redoStack=[],this._maxHistorySize=null!==(e=null==t?void 0:t.maxHistorySize)&&void 0!==e?e:50,this._undoStack=null!==(i=null==t?void 0:t.undoStack)&&void 0!==i?i:[],this._redoStack=null!==(s=null==t?void 0:t.redoStack)&&void 0!==s?s:[]}}export{_ as BitmapLayer,r as CanvasLayer,d as ContentLayerManager,p as DocumentPlugin,y as HistoryManager,m as InteractionPlugin,o as LayerBase,l as LayerManagerBase,w as SnapshotCommand,g as TopScreenLayerManager,h as ViewManager,v as createDocumentPlugin,u as createInteractionPlugin,f as createSnapshotCommand};
@@ -0,0 +1,34 @@
1
+ import { ViewManager } from '../core/view-manager';
2
+ import { SpaceType } from '../types';
3
+ import { LayerBase } from './layer.base';
4
+ import { BitmapLayer, ICreateBitmapLayerOptions } from './layer.bitmap';
5
+ import { CanvasLayer, ICreateCanvasLayerOption } from './layer.canvas';
6
+ declare class LayerManagerBase {
7
+ protected _worldLayers: LayerBase[];
8
+ protected _screenLayers: LayerBase[];
9
+ /**
10
+ * Render all layers in target view.
11
+ */
12
+ protected _renderAllLayersIn(view: ViewManager, context: CanvasRenderingContext2D): void;
13
+ addLayer(layer: LayerBase, insertAt?: number): string;
14
+ createImageLayer(option: ICreateBitmapLayerOptions): Promise<BitmapLayer>;
15
+ createCanvasLayer(option: ICreateCanvasLayerOption): CanvasLayer;
16
+ removeLayer(id: string): void;
17
+ /**
18
+ * Move a layer from one index to another.
19
+ * Both indices are relative to the layer's space (world or screen).
20
+ */
21
+ moveLayer(id: string, toIndex: number): void;
22
+ getLayer(id: string): LayerBase | undefined;
23
+ getAllLayers(space?: SpaceType): LayerBase[];
24
+ /**
25
+ * Hit test all layers (top-first).
26
+ *
27
+ * @param x
28
+ * @param y
29
+ * @param space
30
+ */
31
+ hitTest(x: number, y: number, space?: 'world' | 'screen'): LayerBase | undefined;
32
+ destroy(): void;
33
+ }
34
+ export { LayerManagerBase };
@@ -0,0 +1,46 @@
1
+ import { ViewManager } from '../core/view-manager';
2
+ import { LayerManagerBase } from './layer-manager.base';
3
+ /**
4
+ * Content Layer Manager is used to store content bitmaps.
5
+ * Uses a composite cache to avoid re-rendering all layers every frame.
6
+ */
7
+ declare class ContentLayerManager extends LayerManagerBase {
8
+ private _compositeCache;
9
+ private _compositeCacheCtx;
10
+ private _compositeDirty;
11
+ private _lastCacheWidth;
12
+ private _lastCacheHeight;
13
+ private _cachedBoundsMinX;
14
+ private _cachedBoundsMinY;
15
+ /**
16
+ * Mark the composite cache as dirty.
17
+ * Call this when any layer content changes.
18
+ */
19
+ markDirty(): void;
20
+ /**
21
+ * Override addLayer to mark cache dirty.
22
+ */
23
+ addLayer(layer: import('./layer.base').LayerBase, insertAt?: number): string;
24
+ /**
25
+ * Override removeLayer to mark cache dirty.
26
+ */
27
+ removeLayer(id: string): void;
28
+ /**
29
+ * Override moveLayer to mark cache dirty.
30
+ */
31
+ moveLayer(id: string, toIndex: number): void;
32
+ /**
33
+ * Render all layers in target view.
34
+ * Uses composite cache for better performance with many layers.
35
+ */
36
+ renderAllLayersIn(view: ViewManager): void;
37
+ /**
38
+ * Rebuild the composite cache from all world layers.
39
+ */
40
+ private _rebuildCompositeCache;
41
+ /**
42
+ * Override destroy to clean up cache.
43
+ */
44
+ destroy(): void;
45
+ }
46
+ export { ContentLayerManager };
@@ -0,0 +1,13 @@
1
+ import { ViewManager } from '../core/view-manager';
2
+ import { LayerManagerBase } from './layer-manager.base';
3
+ /**
4
+ * Top Screen Layer Manager is used to store top screen overlays.
5
+ * If you have something like UI elements, just put it here.
6
+ */
7
+ declare class TopScreenLayerManager extends LayerManagerBase {
8
+ /**
9
+ * Render all layers in target view.
10
+ */
11
+ renderAllLayersIn(view: ViewManager): void;
12
+ }
13
+ export { TopScreenLayerManager };
@@ -0,0 +1,16 @@
1
+ import type { ViewManager } from '../core/view-manager';
2
+ import { BlendMode, SpaceType } from '../types';
3
+ declare abstract class LayerBase {
4
+ readonly id: string;
5
+ type: string;
6
+ name: string;
7
+ space: SpaceType;
8
+ visible: boolean;
9
+ opacity: number;
10
+ blend: BlendMode;
11
+ abstract render(context: CanvasRenderingContext2D, view?: ViewManager): void;
12
+ abstract destroy(): void;
13
+ abstract hitTest(x: number, y: number, view?: ViewManager): boolean;
14
+ protected constructor(name: string, type: string, space?: SpaceType);
15
+ }
16
+ export { LayerBase };
@@ -0,0 +1,37 @@
1
+ import { AnchorType, SpaceType } from '../types';
2
+ import { CanvasLayer } from './layer.canvas';
3
+ interface ICreateBitmapLayerOptions {
4
+ src: string | File | Blob;
5
+ width?: number;
6
+ height?: number;
7
+ name?: string;
8
+ x?: number;
9
+ y?: number;
10
+ scale?: number;
11
+ rotation?: number;
12
+ anchor?: AnchorType;
13
+ space?: SpaceType;
14
+ crossOrigin?: '' | 'anonymous' | 'use-credentials';
15
+ }
16
+ /**
17
+ * Image is used to load and display an image in world space.
18
+ */
19
+ declare class BitmapLayer extends CanvasLayer {
20
+ #private;
21
+ /** 从图片源创建位图层(会把像素绘入离屏) */
22
+ static fromImage(options: ICreateBitmapLayerOptions): Promise<BitmapLayer>;
23
+ /** 替换图源(尺寸会重配) */
24
+ setSource(src: string | File | Blob, crossOrigin?: '' | 'anonymous' | 'use-credentials'): Promise<void>;
25
+ /** 在位图上作画(提供 ctx) */
26
+ paint(fn: (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => void): void;
27
+ /** 读取/写回像素 */
28
+ getImageData(sx?: number, sy?: number, sw?: number, sh?: number): ImageData;
29
+ putImageData(img: ImageData, dx?: number, dy?: number): void;
30
+ /** 导出(PNG dataURL 或 ImageBitmap) */
31
+ toDataURL(type?: string, quality?: number): string;
32
+ toImageBitmap(opts?: ImageBitmapOptions): Promise<ImageBitmap>;
33
+ destroy(): void;
34
+ private constructor();
35
+ }
36
+ export { BitmapLayer };
37
+ export type { ICreateBitmapLayerOptions };
@@ -0,0 +1,116 @@
1
+ import type { ViewManager } from '../core/view-manager';
2
+ import { AnchorType, SpaceType } from '../types';
3
+ import { LayerBase } from './layer.base';
4
+ interface ICreateCanvasLayerOption {
5
+ name?: string;
6
+ width: number;
7
+ height: number;
8
+ /**
9
+ * The space of the layer, either 'world' or 'screen'.
10
+ *
11
+ * @default 'world'
12
+ */
13
+ space?: SpaceType;
14
+ x?: number;
15
+ y?: number;
16
+ scale?: number;
17
+ rotation?: number;
18
+ anchor?: AnchorType;
19
+ redraw?: (context: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => void;
20
+ }
21
+ interface ILayerResizeConfig {
22
+ width: number;
23
+ height: number;
24
+ }
25
+ interface IRect {
26
+ x: number;
27
+ y: number;
28
+ width: number;
29
+ height: number;
30
+ }
31
+ /**
32
+ * CanvasLayer is used to create a layer with an offscreen canvas in either world or screen space.
33
+ * You can draw anything you want on the offscreen canvas, and it will be rendered as a layer.
34
+ */
35
+ declare class CanvasLayer extends LayerBase {
36
+ private readonly _redraw?;
37
+ readonly canvas: HTMLCanvasElement;
38
+ readonly context: CanvasRenderingContext2D;
39
+ x: number;
40
+ y: number;
41
+ scale: number;
42
+ rotation: number;
43
+ anchor: AnchorType;
44
+ private _drawing;
45
+ private _lastX;
46
+ private _lastY;
47
+ /**
48
+ * Begin a stroke at the given world coordinates.
49
+ */
50
+ beginStroke(wx: number, wy: number): void;
51
+ /**
52
+ * Continue a stroke to the given world coordinates.
53
+ */
54
+ stroke(wx: number, wy: number, color: string, size: number, pressure?: number, mode?: 'brush' | 'eraser'): void;
55
+ /**
56
+ * End the current stroke.
57
+ */
58
+ endStroke(): void;
59
+ /**
60
+ * Check if currently drawing.
61
+ */
62
+ isDrawing(): boolean;
63
+ /**
64
+ * Capture a snapshot of the canvas content.
65
+ * If region is provided, only that region is captured (more memory efficient).
66
+ * Returns null if capture fails.
67
+ */
68
+ captureSnapshot(region?: IRect): ImageData | null;
69
+ /**
70
+ * Restore a snapshot to the canvas.
71
+ * If position is provided, the snapshot is placed at that position.
72
+ * Otherwise, it's placed at (0, 0).
73
+ */
74
+ restoreSnapshot(data: ImageData, position?: {
75
+ x: number;
76
+ y: number;
77
+ }): void;
78
+ /**
79
+ * Clear a region of the canvas.
80
+ * If no region is provided, clears the entire canvas.
81
+ */
82
+ clearRegion(region?: IRect): void;
83
+ /**
84
+ * Request a redraw of the offscreen canvas.
85
+ * This will call the redraw function you provided in the constructor.
86
+ */
87
+ requestRedraw(): void;
88
+ /**
89
+ * Draw an image onto the offscreen canvas.
90
+ */
91
+ drawImage(image: HTMLImageElement | HTMLCanvasElement | ImageBitmap, dx: number, dy: number, dw?: number, dh?: number): void;
92
+ /**
93
+ * Hit test the layer.
94
+ * Returns true if the point (wx, wy) in world space is within the layer bounds.
95
+ */
96
+ hitTest(wx: number, wy: number): boolean;
97
+ /**
98
+ * Convert a point from world space to local layer space.
99
+ */
100
+ toLocalPoint(wx: number, wy: number): {
101
+ lx: number;
102
+ ly: number;
103
+ };
104
+ /**
105
+ * Render the layer onto the given context.
106
+ */
107
+ render(ctx: CanvasRenderingContext2D, view?: ViewManager): void;
108
+ cropTo(config: ILayerResizeConfig): void;
109
+ resizeTo(config: ILayerResizeConfig): void;
110
+ private _cloneCanvas;
111
+ private _setCanvasSize;
112
+ destroy(): void;
113
+ constructor(options: ICreateCanvasLayerOption);
114
+ }
115
+ export { CanvasLayer };
116
+ export type { ICreateCanvasLayerOption, ILayerResizeConfig, IRect };