@lancercomet/zoom-pan 0.3.0 → 0.4.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 CHANGED
@@ -1,18 +1,35 @@
1
1
  # ZoomPan
2
2
 
3
- A lightweight **canvas viewport control library** for 2D applications.
4
- Provides smooth **panning**, **zooming**, **pinch-to-zoom**, and **inertia** behaviors.
3
+
4
+ ZoomPan is a **2D rendering viewport / render-pipeline library**.
5
+
6
+ It is **not** a scene-graph engine. If you are looking for a full display tree, rich shape primitives, filters, batching, and a large ecosystem, you probably want **PixiJS** / **Fabric.js** / **Konva**.
7
+
8
+ It provides a consistent world/screen coordinate model, pan/zoom interactions, a pluggable render pipeline (RenderPipeline/Pass), a layer system, and optional rendering backends (Canvas2D/WebGL).
9
+
10
+ Use it as a simple “image viewer with pan & zoom”, or as the rendering core of a “drawing app / annotation tool / editor”.
11
+
12
+ ## ZoomPan vs Pixi / Fabric / Konva
13
+
14
+ | Topic | ZoomPan | Pixi / Fabric / Konva |
15
+ | --- | --- | --- |
16
+ | Primary goal | Viewport + render orchestration for editor-style apps | General-purpose 2D rendering engine / scene graph |
17
+ | Best for | Zoom/pan canvas viewers, drawing apps, editors, annotation tools | Complex object trees, shape primitives, filters, rich rendering features |
18
+ | Extension model | Explicit render pipeline passes + plugins | Scene graph + framework lifecycle + ecosystem plugins |
19
+ | World/screen split | Built-in phases: `world` and `screen` | Often requires custom conventions / layers / containers |
20
+ | What it does **not** try to be | A full engine with display tree, batching, filters | A small viewport kernel |
5
21
 
6
22
  ## Features
7
23
 
8
- - Mouse pan & wheel zoom
9
- - Touch pan & pinch-to-zoom (always works, even in drawing mode)
10
- - Pen/stylus support for drawing apps
11
- - Layer-based rendering system
12
- - Built-in undo/redo support
13
- - Document bounds with margins
24
+ - **Selectable renderer backend**: `renderer: 'canvas2d' | 'webgl' | 'auto' | IRenderer`
25
+ - **Pluggable render pipeline**: 4 phases `beforeWorld / world / afterWorld / screen` + stable ordering via `order`
26
+ - **Viewport transform**: pan/zoom + world screen coordinate conversion
27
+ - **Interaction plugin**: mouse/touch/pen input + inertia + `cancel()` for focus-loss cleanup
28
+ - **Layer system**: split world/screen rendering (you can insert passes between them)
29
+ - **Bounds (document bounds)**: background/shadow/clip/border, pan clamping, `zoomToFit({ maxScale })`
30
+ - **History (Undo/Redo)**: provided via plugin/examples (see `examples/`)
14
31
 
15
- ## Installation
32
+ ## Install
16
33
 
17
34
  ```bash
18
35
  npm install @lancercomet/zoom-pan
@@ -22,27 +39,28 @@ npm install @lancercomet/zoom-pan
22
39
 
23
40
  ### Image Viewer
24
41
 
25
- ```typescript
42
+ ```ts
26
43
  import { ViewManager, ContentLayerManager, createInteractionPlugin } from '@lancercomet/zoom-pan'
27
44
 
28
- const layerManager = new ContentLayerManager()
45
+ const content = new ContentLayerManager()
29
46
 
30
- const view = new ViewManager(canvas)
47
+ const view = new ViewManager(canvas, {
48
+ renderer: 'auto' // 'canvas2d' | 'webgl' | 'auto'
49
+ })
31
50
 
32
- view.registerLayerManager(layerManager)
51
+ view.registerLayerManager(content)
33
52
  view.use(createInteractionPlugin())
34
53
 
35
- // Add an image
36
- await layerManager.createImageLayer({
54
+ await content.createImageLayer({
37
55
  src: 'image.png',
38
56
  x: 0,
39
57
  y: 0
40
58
  })
41
59
  ```
42
60
 
43
- ### Drawing App
61
+ ### Drawing App (simplified)
44
62
 
45
- ```typescript
63
+ ```ts
46
64
  import {
47
65
  ViewManager,
48
66
  ContentLayerManager,
@@ -53,41 +71,39 @@ import {
53
71
  createSnapshotCommand
54
72
  } from '@lancercomet/zoom-pan'
55
73
 
56
- const layerManager = new ContentLayerManager()
57
- const historyManager = new HistoryManager({ maxHistorySize: 50 })
58
-
59
- const view = new ViewManager(canvas)
74
+ const content = new ContentLayerManager()
75
+ const history = new HistoryManager({ maxHistorySize: 50 })
60
76
 
61
- view.registerLayerManager(layerManager)
77
+ const view = new ViewManager(canvas, { renderer: 'auto' })
78
+ view.registerLayerManager(content)
62
79
 
63
- // Interaction plugin: controls pan/zoom
80
+ // Interaction plugin controls pan/zoom.
64
81
  // - Touch gestures (pan/pinch) always work
65
- // - Mouse/pen pan controlled by setPanEnabled()
82
+ // - Mouse/pen pan is controlled by setPanEnabled()
66
83
  const interaction = view.use(createInteractionPlugin())
67
84
 
68
- // Document plugin: sets canvas bounds
69
- const doc = view.use(createBoundsPlugin({
85
+ // Bounds defines document rect/margins and clamps pan.
86
+ const bounds = view.use(createBoundsPlugin({
70
87
  rect: { x: 0, y: 0, width: 1200, height: 800 },
71
88
  margins: { left: 50, right: 50, top: 50, bottom: 50 }
72
89
  }))
73
- doc.zoomToFit()
74
90
 
75
- // Create drawable layer
76
- const drawLayer = new CanvasLayer({
77
- width: 1200,
78
- height: 800
79
- })
80
- layerManager.addLayer(drawLayer)
91
+ // Prevent upscaling small documents
92
+ bounds.zoomToFit({ maxScale: 1 })
81
93
 
82
- // Switch to drawing mode (disable mouse/pen pan, touch still works)
94
+ const drawLayer = new CanvasLayer({ width: 1200, height: 800 })
95
+ content.addLayer(drawLayer)
96
+
97
+ // Drawing mode: disable mouse/pen pan (touch still works)
83
98
  interaction.setPanEnabled(false)
84
99
 
85
- // Drawing with undo support
100
+ // On focus loss, cancel any ongoing drag/pinch/inertia
101
+ window.addEventListener('blur', () => interaction.cancel())
102
+
86
103
  let snapshotBefore: ImageData | null = null
87
104
 
88
105
  canvas.onpointerdown = (e) => {
89
- if (e.pointerType === 'touch') return // Let plugin handle touch
90
-
106
+ if (e.pointerType === 'touch') return
91
107
  const { wx, wy } = view.toWorld(e.offsetX, e.offsetY)
92
108
  snapshotBefore = drawLayer.captureSnapshot()
93
109
  drawLayer.beginStroke(wx, wy)
@@ -96,7 +112,6 @@ canvas.onpointerdown = (e) => {
96
112
  canvas.onpointermove = (e) => {
97
113
  if (e.pointerType === 'touch') return
98
114
  if (e.buttons !== 1) return
99
-
100
115
  const { wx, wy } = view.toWorld(e.offsetX, e.offsetY)
101
116
  drawLayer.stroke(wx, wy, '#000', 10, e.pressure, 'brush')
102
117
  view.requestRender()
@@ -106,13 +121,7 @@ canvas.onpointerup = () => {
106
121
  drawLayer.endStroke()
107
122
  const snapshotAfter = drawLayer.captureSnapshot()
108
123
  const cmd = createSnapshotCommand(drawLayer, snapshotBefore, snapshotAfter)
109
- if (cmd) historyManager.addCommand(cmd)
110
- }
111
-
112
- // Undo/Redo
113
- document.onkeydown = (e) => {
114
- if (e.ctrlKey && e.key === 'z') historyManager.undo()
115
- if (e.ctrlKey && e.key === 'y') historyManager.redo()
124
+ if (cmd) history.addCommand(cmd)
116
125
  }
117
126
  ```
118
127
 
@@ -120,102 +129,134 @@ document.onkeydown = (e) => {
120
129
 
121
130
  ### ViewManager
122
131
 
123
- The main viewport controller. Handles coordinate transformation and rendering.
132
+ The main controller: render loop, coordinate transforms, renderer backend, render pipeline, and plugins.
124
133
 
125
- ```typescript
134
+ ```ts
126
135
  const view = new ViewManager(canvas, {
127
136
  minZoom: 0.2,
128
137
  maxZoom: 10,
129
- background: '#fff'
138
+ background: '#fff',
139
+ renderer: 'auto'
130
140
  })
131
141
 
132
- // Coordinate conversion
133
142
  const { wx, wy } = view.toWorld(screenX, screenY)
134
- const { sx, sy } = view.toScreen(worldX, worldY)
143
+ const { x, y } = view.toScreen(worldX, worldY)
135
144
 
136
- // Programmatic zoom
137
145
  view.zoomToAtScreen(anchorX, anchorY, 2.0)
138
146
  view.zoomByFactorAtScreen(anchorX, anchorY, 1.5)
139
147
  ```
140
148
 
149
+ ### Renderer Selection
150
+
151
+ ```ts
152
+ import { ViewManager } from '@lancercomet/zoom-pan'
153
+
154
+ new ViewManager(canvas, { renderer: 'canvas2d' })
155
+ new ViewManager(canvas, { renderer: 'webgl' })
156
+ new ViewManager(canvas, { renderer: 'auto' }) // tries WebGL first, falls back to Canvas2D
157
+ ```
158
+
159
+ ### RenderPipeline / Pass
160
+
161
+ You can insert your own passes (e.g. draw a debug grid before world rendering):
162
+
163
+ ```ts
164
+ view.addRenderPass({
165
+ name: 'debug.grid',
166
+ phase: 'beforeWorld',
167
+ order: -50,
168
+ render: ({ renderer }) => {
169
+ const ctx = renderer.getContentContext()
170
+ ctx.save()
171
+ ctx.strokeStyle = 'rgba(0,0,0,0.08)'
172
+ ctx.lineWidth = 1
173
+ // draw grid / guides here
174
+ ctx.restore()
175
+ }
176
+ })
177
+ ```
178
+
141
179
  ### InteractionPlugin
142
180
 
143
- Handles user input for pan and zoom.
181
+ ```ts
182
+ import { createInteractionPlugin } from '@lancercomet/zoom-pan'
144
183
 
145
- ```typescript
146
184
  const interaction = view.use(createInteractionPlugin())
147
185
 
148
- // Pan enabled affects mouse/pen only
149
- // Touch gestures (single-finger pan, two-finger pinch) always work
150
- interaction.setPanEnabled(true) // Mouse/pen can pan
151
- interaction.setPanEnabled(false) // Mouse/pen cannot pan (for drawing mode)
186
+ interaction.setPanEnabled(true) // mouse/pen can pan
187
+ interaction.setPanEnabled(false) // mouse/pen cannot pan (drawing mode)
152
188
 
153
- interaction.setZoomEnabled(true) // Wheel zoom & pinch zoom enabled
189
+ interaction.setZoomEnabled(true) // wheel zoom + pinch zoom
190
+
191
+ interaction.cancel() // cancel drag/pinch/inertia (blur/visibilitychange)
154
192
  ```
155
193
 
156
194
  ### BoundsPlugin
157
195
 
158
- Defines document bounds and provides pan clamping.
196
+ BoundsPlugin draws background/shadow, clips the world, and draws border via pipeline passes.
197
+ It also supports per-instance pass ordering (useful when you need to insert your own passes around the bounds clip).
198
+
199
+ ```ts
200
+ import { createBoundsPlugin } from '@lancercomet/zoom-pan'
159
201
 
160
- ```typescript
161
- const doc = view.use(createBoundsPlugin({
202
+ const bounds = view.use(createBoundsPlugin({
162
203
  rect: { x: 0, y: 0, width: 1200, height: 800 },
163
204
  margins: { left: 50, right: 50, top: 50, bottom: 50 },
164
205
  drawBorder: true,
165
206
  background: '#f0f0f0',
166
- shadow: { blur: 20, color: 'rgba(0,0,0,0.3)', offsetX: 0, offsetY: 5 }
207
+ shadow: { blur: 20, color: 'rgba(0,0,0,0.3)', offsetX: 0, offsetY: 5 },
208
+ passOrder: {
209
+ beforeWorld: -100,
210
+ afterWorld: 100
211
+ }
167
212
  }))
168
213
 
169
- doc.zoomToFit() // Fit document to viewport
170
- doc.setPanClampMode('minVisible') // 'margin' | 'minVisible'
214
+ bounds.zoomToFit({ maxScale: 1 })
215
+ bounds.setPanClampMode('minVisible') // 'margin' | 'minVisible'
171
216
  ```
172
217
 
173
- ### Layer System
218
+ ### Layers
174
219
 
175
- ```typescript
176
- // Content layers (world space)
177
- const contentManager = new ContentLayerManager()
178
- view.registerLayerManager(contentManager)
220
+ ```ts
221
+ import { ViewManager, ContentLayerManager, TopScreenLayerManager, CanvasLayer } from '@lancercomet/zoom-pan'
179
222
 
180
- // Canvas layer for drawing
181
- const layer = new CanvasLayer({ width: 1200, height: 800 })
182
- contentManager.addLayer(layer)
223
+ const view = new ViewManager(canvas, { renderer: 'auto' })
183
224
 
184
- // Image layer
185
- const imgLayer = await contentManager.createImageLayer({ src: 'image.png' })
225
+ // World-space content
226
+ const content = new ContentLayerManager()
227
+ view.registerLayerManager(content)
186
228
 
187
- // Remove layer (destroys it)
188
- contentManager.removeLayer(layer.id)
229
+ // Screen-space overlay (UI / cursor / HUD)
230
+ const overlay = new TopScreenLayerManager()
231
+ view.registerLayerManager(overlay)
189
232
 
190
- // Detach layer (keeps it for undo)
191
- contentManager.detachLayer(layer.id)
233
+ content.addLayer(new CanvasLayer({ width: 1200, height: 800 }))
192
234
  ```
193
235
 
194
- ### History (Undo/Redo)
236
+ Default rendering is split by pipeline phase:
195
237
 
196
- ```typescript
197
- const history = new HistoryManager({ maxHistorySize: 50 })
238
+ - `world`: calls each LayerManager's `renderWorldLayersIn(view)`
239
+ - `screen`: calls each LayerManager's `renderScreenLayersIn(view)`
240
+
241
+ This means you can insert passes between world and screen (e.g. selection overlay after world, UI in screen).
242
+
243
+ ## Plugin Lifecycle
198
244
 
199
- // Snapshot-based undo for drawing
200
- const before = layer.captureSnapshot()
201
- // ... draw ...
202
- const after = layer.captureSnapshot()
203
- const cmd = createSnapshotCommand(layer, before, after)
204
- if (cmd) history.addCommand(cmd)
245
+ `ViewManager.use(plugin)` is transactional: if `install()` throws, it performs a best-effort rollback to avoid leaving a half-installed plugin behind.
205
246
 
206
- // Layer commands
207
- import { CreateLayerCommand, DeleteLayerCommand } from '@lancercomet/zoom-pan'
247
+ ```ts
248
+ view.hasPlugin('bounds')
249
+ view.listPlugins()
250
+ view.clearPlugins()
208
251
 
209
- history.undo()
210
- history.redo()
211
- history.canUndo()
212
- history.canRedo()
252
+ view.unuse('bounds')
213
253
  ```
214
254
 
215
255
  ## Examples
216
256
 
217
- See `examples/` folder:
257
+ See `examples/`:
218
258
 
219
- - **`examples/viewer/`** - Simple image viewer
220
- - **`examples/painter/`** - Full drawing app with brush, eraser, layers, undo/redo
221
- - **`examples/doc/`** - Document mode demo with background and shadow
259
+ - `examples/viewer/`: image viewer
260
+ - `examples/painter/`: drawing app (brush/eraser/layers/undo-redo)
261
+ - `examples/bounds/`: bounds (background/shadow/zoomToFit)
262
+ - `examples/history/`: history (undo/redo)
@@ -0,0 +1,24 @@
1
+ import type { IRenderer } from './renderer/types';
2
+ import type { ViewManager } from './view-manager';
3
+ export type RenderPhase = 'beforeWorld' | 'world' | 'afterWorld' | 'screen';
4
+ export interface RenderContext {
5
+ view: ViewManager;
6
+ renderer: IRenderer;
7
+ }
8
+ export interface RenderPass {
9
+ readonly name: string;
10
+ readonly phase: RenderPhase;
11
+ readonly order?: number;
12
+ enabled?: boolean;
13
+ render(ctx: RenderContext): void;
14
+ }
15
+ declare class RenderPipeline {
16
+ private readonly _passes;
17
+ addPass(pass: RenderPass): void;
18
+ removePass(name: string): void;
19
+ getPass(name: string): RenderPass | undefined;
20
+ listPasses(): RenderPass[];
21
+ renderPhase(phase: RenderPhase, ctx: RenderContext): void;
22
+ private _sort;
23
+ }
24
+ export { RenderPipeline };
@@ -0,0 +1,40 @@
1
+ import type { IRenderer, IRendererTransform } from './types';
2
+ /**
3
+ * Canvas 2D Renderer - the default renderer using Canvas 2D API.
4
+ */
5
+ declare class Canvas2DRenderer implements IRenderer {
6
+ readonly type: "canvas2d";
7
+ private _canvas;
8
+ private _ctx;
9
+ private _contentCanvas;
10
+ private _contentCtx;
11
+ private _topScreenCanvas;
12
+ private _topScreenCtx;
13
+ private _dpr;
14
+ private _currentTransform;
15
+ init(canvas: HTMLCanvasElement): void;
16
+ dispose(): void;
17
+ resize(width: number, height: number, dpr: number): void;
18
+ clear(color: string | null): void;
19
+ beginFrame(): void;
20
+ endFrame(): void;
21
+ setWorldTransform(transform: IRendererTransform): void;
22
+ setScreenTransform(dpr: number): void;
23
+ drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number, opacity?: number): void;
24
+ fillRect(x: number, y: number, w: number, h: number, color: string): void;
25
+ strokeRect(x: number, y: number, w: number, h: number, color: string, lineWidth?: number): void;
26
+ save(): void;
27
+ restore(): void;
28
+ clipRect(x: number, y: number, w: number, h: number): void;
29
+ getPixelColor(sx: number, sy: number): {
30
+ r: number;
31
+ g: number;
32
+ b: number;
33
+ a: number;
34
+ };
35
+ getContentContext(): CanvasRenderingContext2D;
36
+ getTopScreenContext(): CanvasRenderingContext2D;
37
+ get contentCanvas(): HTMLCanvasElement | null;
38
+ get topScreenCanvas(): HTMLCanvasElement | null;
39
+ }
40
+ export { Canvas2DRenderer };
@@ -0,0 +1,3 @@
1
+ export * from './types';
2
+ export * from './canvas2d-renderer';
3
+ export * from './webgl-renderer';
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Renderer interface - abstract away Canvas2D vs WebGL implementation.
3
+ */
4
+ export interface IRendererTransform {
5
+ zoom: number;
6
+ tx: number;
7
+ ty: number;
8
+ dpr: number;
9
+ }
10
+ export interface IRendererSize {
11
+ width: number;
12
+ height: number;
13
+ }
14
+ /**
15
+ * Abstract renderer interface.
16
+ * Both Canvas2DRenderer and WebGLRenderer implement this.
17
+ */
18
+ export interface IRenderer {
19
+ /**
20
+ * Renderer type identifier.
21
+ */
22
+ readonly type: 'canvas2d' | 'webgl';
23
+ /**
24
+ * Initialize the renderer with the target canvas.
25
+ */
26
+ init(canvas: HTMLCanvasElement): void;
27
+ /**
28
+ * Dispose all resources.
29
+ */
30
+ dispose(): void;
31
+ /**
32
+ * Resize internal buffers to match canvas size.
33
+ */
34
+ resize(width: number, height: number, dpr: number): void;
35
+ /**
36
+ * Clear the canvas with background color.
37
+ * @param color - CSS color string, null for transparent
38
+ */
39
+ clear(color: string | null): void;
40
+ /**
41
+ * Begin a new frame.
42
+ * Prepares internal state for rendering.
43
+ */
44
+ beginFrame(): void;
45
+ /**
46
+ * End frame and composite to screen.
47
+ */
48
+ endFrame(): void;
49
+ /**
50
+ * Set the current transform for world-space rendering.
51
+ */
52
+ setWorldTransform(transform: IRendererTransform): void;
53
+ /**
54
+ * Set transform for screen-space rendering (no zoom/pan, just DPR).
55
+ */
56
+ setScreenTransform(dpr: number): void;
57
+ /**
58
+ * Draw an image/canvas at the specified world position.
59
+ */
60
+ drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number, opacity?: number): void;
61
+ /**
62
+ * Draw a rect (for backgrounds, borders, etc.)
63
+ */
64
+ fillRect(x: number, y: number, w: number, h: number, color: string): void;
65
+ /**
66
+ * Draw a stroked rect.
67
+ */
68
+ strokeRect(x: number, y: number, w: number, h: number, color: string, lineWidth?: number): void;
69
+ /**
70
+ * Save the current state (transform, clip, etc.)
71
+ */
72
+ save(): void;
73
+ /**
74
+ * Restore the previously saved state.
75
+ */
76
+ restore(): void;
77
+ /**
78
+ * Set a rectangular clip region.
79
+ */
80
+ clipRect(x: number, y: number, w: number, h: number): void;
81
+ /**
82
+ * Get pixel color at screen position.
83
+ * Returns RGBA values.
84
+ */
85
+ getPixelColor(sx: number, sy: number): {
86
+ r: number;
87
+ g: number;
88
+ b: number;
89
+ a: number;
90
+ };
91
+ /**
92
+ * Get the content canvas/context for direct drawing (Canvas2D only).
93
+ * For WebGL, this returns a temporary 2D canvas that gets uploaded as texture.
94
+ */
95
+ getContentContext(): CanvasRenderingContext2D;
96
+ /**
97
+ * Get the top screen canvas/context for UI overlays.
98
+ */
99
+ getTopScreenContext(): CanvasRenderingContext2D;
100
+ }
101
+ /**
102
+ * Renderer type option for ViewManager.
103
+ */
104
+ export type RendererType = 'canvas2d' | 'webgl' | 'auto' | IRenderer;
@@ -0,0 +1,64 @@
1
+ import type { IRenderer, IRendererTransform } from './types';
2
+ /**
3
+ * WebGL Renderer - GPU accelerated rendering using WebGL.
4
+ */
5
+ declare class WebGLRenderer implements IRenderer {
6
+ readonly type: "webgl";
7
+ /**
8
+ * Check if WebGL is supported in the current browser.
9
+ */
10
+ static isSupported(): boolean;
11
+ private _canvas;
12
+ private _gl;
13
+ private _contentCanvas;
14
+ private _contentCtx;
15
+ private _topScreenCanvas;
16
+ private _topScreenCtx;
17
+ private _textureProgram;
18
+ private _colorProgram;
19
+ private _positionBuffer;
20
+ private _texCoordBuffer;
21
+ private _textureCache;
22
+ private _contentTexture;
23
+ private _topScreenTexture;
24
+ private _dpr;
25
+ private _width;
26
+ private _height;
27
+ private _currentTransform;
28
+ private _stateStack;
29
+ private _isContextLost;
30
+ private _onContextLost;
31
+ private _onContextRestored;
32
+ init(canvas: HTMLCanvasElement): void;
33
+ private _initWebGL;
34
+ private _createShader;
35
+ private _createProgram;
36
+ private _createTexture;
37
+ private _updateTexture;
38
+ dispose(): void;
39
+ resize(width: number, height: number, dpr: number): void;
40
+ clear(color: string | null): void;
41
+ private _parseColor;
42
+ beginFrame(): void;
43
+ endFrame(): void;
44
+ private _drawFullScreenQuad;
45
+ setWorldTransform(transform: IRendererTransform): void;
46
+ setScreenTransform(dpr: number): void;
47
+ drawImage(source: CanvasImageSource, dx: number, dy: number, dw: number, dh: number, opacity?: number): void;
48
+ fillRect(x: number, y: number, w: number, h: number, color: string): void;
49
+ strokeRect(x: number, y: number, w: number, h: number, color: string, lineWidth?: number): void;
50
+ save(): void;
51
+ restore(): void;
52
+ clipRect(x: number, y: number, w: number, h: number): void;
53
+ getPixelColor(sx: number, sy: number): {
54
+ r: number;
55
+ g: number;
56
+ b: number;
57
+ a: number;
58
+ };
59
+ getContentContext(): CanvasRenderingContext2D;
60
+ getTopScreenContext(): CanvasRenderingContext2D;
61
+ get contentCanvas(): HTMLCanvasElement | null;
62
+ get topScreenCanvas(): HTMLCanvasElement | null;
63
+ }
64
+ export { WebGLRenderer };