@lancercomet/zoom-pan 0.2.2 → 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,76 +39,71 @@ 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, () => {
31
- layerManager.renderAllLayersIn(view)
47
+ const view = new ViewManager(canvas, {
48
+ renderer: 'auto' // 'canvas2d' | 'webgl' | 'auto'
32
49
  })
33
50
 
34
- view.registerLayerManager(layerManager)
51
+ view.registerLayerManager(content)
35
52
  view.use(createInteractionPlugin())
36
53
 
37
- // Add an image
38
- await layerManager.createImageLayer({
54
+ await content.createImageLayer({
39
55
  src: 'image.png',
40
56
  x: 0,
41
57
  y: 0
42
58
  })
43
59
  ```
44
60
 
45
- ### Drawing App
61
+ ### Drawing App (simplified)
46
62
 
47
- ```typescript
63
+ ```ts
48
64
  import {
49
65
  ViewManager,
50
66
  ContentLayerManager,
51
67
  CanvasLayer,
52
68
  HistoryManager,
53
69
  createInteractionPlugin,
54
- createDocumentPlugin,
70
+ createBoundsPlugin,
55
71
  createSnapshotCommand
56
72
  } from '@lancercomet/zoom-pan'
57
73
 
58
- const layerManager = new ContentLayerManager()
59
- const historyManager = new HistoryManager({ maxHistorySize: 50 })
60
-
61
- const view = new ViewManager(canvas, () => {
62
- layerManager.renderAllLayersIn(view)
63
- })
74
+ const content = new ContentLayerManager()
75
+ const history = new HistoryManager({ maxHistorySize: 50 })
64
76
 
65
- view.registerLayerManager(layerManager)
77
+ const view = new ViewManager(canvas, { renderer: 'auto' })
78
+ view.registerLayerManager(content)
66
79
 
67
- // Interaction plugin: controls pan/zoom
80
+ // Interaction plugin controls pan/zoom.
68
81
  // - Touch gestures (pan/pinch) always work
69
- // - Mouse/pen pan controlled by setPanEnabled()
82
+ // - Mouse/pen pan is controlled by setPanEnabled()
70
83
  const interaction = view.use(createInteractionPlugin())
71
84
 
72
- // Document plugin: sets canvas bounds
73
- const doc = view.use(createDocumentPlugin({
85
+ // Bounds defines document rect/margins and clamps pan.
86
+ const bounds = view.use(createBoundsPlugin({
74
87
  rect: { x: 0, y: 0, width: 1200, height: 800 },
75
88
  margins: { left: 50, right: 50, top: 50, bottom: 50 }
76
89
  }))
77
- doc.zoomToFit()
78
90
 
79
- // Create drawable layer
80
- const drawLayer = new CanvasLayer({
81
- width: 1200,
82
- height: 800
83
- })
84
- layerManager.addLayer(drawLayer)
91
+ // Prevent upscaling small documents
92
+ bounds.zoomToFit({ maxScale: 1 })
93
+
94
+ const drawLayer = new CanvasLayer({ width: 1200, height: 800 })
95
+ content.addLayer(drawLayer)
85
96
 
86
- // Switch to drawing mode (disable mouse/pen pan, touch still works)
97
+ // Drawing mode: disable mouse/pen pan (touch still works)
87
98
  interaction.setPanEnabled(false)
88
99
 
89
- // Drawing with undo support
100
+ // On focus loss, cancel any ongoing drag/pinch/inertia
101
+ window.addEventListener('blur', () => interaction.cancel())
102
+
90
103
  let snapshotBefore: ImageData | null = null
91
104
 
92
105
  canvas.onpointerdown = (e) => {
93
- if (e.pointerType === 'touch') return // Let plugin handle touch
94
-
106
+ if (e.pointerType === 'touch') return
95
107
  const { wx, wy } = view.toWorld(e.offsetX, e.offsetY)
96
108
  snapshotBefore = drawLayer.captureSnapshot()
97
109
  drawLayer.beginStroke(wx, wy)
@@ -100,7 +112,6 @@ canvas.onpointerdown = (e) => {
100
112
  canvas.onpointermove = (e) => {
101
113
  if (e.pointerType === 'touch') return
102
114
  if (e.buttons !== 1) return
103
-
104
115
  const { wx, wy } = view.toWorld(e.offsetX, e.offsetY)
105
116
  drawLayer.stroke(wx, wy, '#000', 10, e.pressure, 'brush')
106
117
  view.requestRender()
@@ -110,13 +121,7 @@ canvas.onpointerup = () => {
110
121
  drawLayer.endStroke()
111
122
  const snapshotAfter = drawLayer.captureSnapshot()
112
123
  const cmd = createSnapshotCommand(drawLayer, snapshotBefore, snapshotAfter)
113
- if (cmd) historyManager.addCommand(cmd)
114
- }
115
-
116
- // Undo/Redo
117
- document.onkeydown = (e) => {
118
- if (e.ctrlKey && e.key === 'z') historyManager.undo()
119
- if (e.ctrlKey && e.key === 'y') historyManager.redo()
124
+ if (cmd) history.addCommand(cmd)
120
125
  }
121
126
  ```
122
127
 
@@ -124,102 +129,134 @@ document.onkeydown = (e) => {
124
129
 
125
130
  ### ViewManager
126
131
 
127
- The main viewport controller. Handles coordinate transformation and rendering.
132
+ The main controller: render loop, coordinate transforms, renderer backend, render pipeline, and plugins.
128
133
 
129
- ```typescript
130
- const view = new ViewManager(canvas, renderFn, {
134
+ ```ts
135
+ const view = new ViewManager(canvas, {
131
136
  minZoom: 0.2,
132
137
  maxZoom: 10,
133
- background: '#fff'
138
+ background: '#fff',
139
+ renderer: 'auto'
134
140
  })
135
141
 
136
- // Coordinate conversion
137
142
  const { wx, wy } = view.toWorld(screenX, screenY)
138
- const { sx, sy } = view.toScreen(worldX, worldY)
143
+ const { x, y } = view.toScreen(worldX, worldY)
139
144
 
140
- // Programmatic zoom
141
145
  view.zoomToAtScreen(anchorX, anchorY, 2.0)
142
146
  view.zoomByFactorAtScreen(anchorX, anchorY, 1.5)
143
147
  ```
144
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
+
145
179
  ### InteractionPlugin
146
180
 
147
- Handles user input for pan and zoom.
181
+ ```ts
182
+ import { createInteractionPlugin } from '@lancercomet/zoom-pan'
148
183
 
149
- ```typescript
150
184
  const interaction = view.use(createInteractionPlugin())
151
185
 
152
- // Pan enabled affects mouse/pen only
153
- // Touch gestures (single-finger pan, two-finger pinch) always work
154
- interaction.setPanEnabled(true) // Mouse/pen can pan
155
- 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)
188
+
189
+ interaction.setZoomEnabled(true) // wheel zoom + pinch zoom
156
190
 
157
- interaction.setZoomEnabled(true) // Wheel zoom & pinch zoom enabled
191
+ interaction.cancel() // cancel drag/pinch/inertia (blur/visibilitychange)
158
192
  ```
159
193
 
160
- ### DocumentPlugin
194
+ ### BoundsPlugin
195
+
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).
161
198
 
162
- Defines document bounds and provides pan clamping.
199
+ ```ts
200
+ import { createBoundsPlugin } from '@lancercomet/zoom-pan'
163
201
 
164
- ```typescript
165
- const doc = view.use(createDocumentPlugin({
202
+ const bounds = view.use(createBoundsPlugin({
166
203
  rect: { x: 0, y: 0, width: 1200, height: 800 },
167
204
  margins: { left: 50, right: 50, top: 50, bottom: 50 },
168
205
  drawBorder: true,
169
206
  background: '#f0f0f0',
170
- 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
+ }
171
212
  }))
172
213
 
173
- doc.zoomToFit() // Fit document to viewport
174
- doc.setPanClampMode('minVisible') // 'margin' | 'minVisible'
214
+ bounds.zoomToFit({ maxScale: 1 })
215
+ bounds.setPanClampMode('minVisible') // 'margin' | 'minVisible'
175
216
  ```
176
217
 
177
- ### Layer System
218
+ ### Layers
178
219
 
179
- ```typescript
180
- // Content layers (world space)
181
- const contentManager = new ContentLayerManager()
182
- view.registerLayerManager(contentManager)
220
+ ```ts
221
+ import { ViewManager, ContentLayerManager, TopScreenLayerManager, CanvasLayer } from '@lancercomet/zoom-pan'
183
222
 
184
- // Canvas layer for drawing
185
- const layer = new CanvasLayer({ width: 1200, height: 800 })
186
- contentManager.addLayer(layer)
223
+ const view = new ViewManager(canvas, { renderer: 'auto' })
187
224
 
188
- // Image layer
189
- const imgLayer = await contentManager.createImageLayer({ src: 'image.png' })
225
+ // World-space content
226
+ const content = new ContentLayerManager()
227
+ view.registerLayerManager(content)
190
228
 
191
- // Remove layer (destroys it)
192
- contentManager.removeLayer(layer.id)
229
+ // Screen-space overlay (UI / cursor / HUD)
230
+ const overlay = new TopScreenLayerManager()
231
+ view.registerLayerManager(overlay)
193
232
 
194
- // Detach layer (keeps it for undo)
195
- contentManager.detachLayer(layer.id)
233
+ content.addLayer(new CanvasLayer({ width: 1200, height: 800 }))
196
234
  ```
197
235
 
198
- ### History (Undo/Redo)
236
+ Default rendering is split by pipeline phase:
199
237
 
200
- ```typescript
201
- 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
202
244
 
203
- // Snapshot-based undo for drawing
204
- const before = layer.captureSnapshot()
205
- // ... draw ...
206
- const after = layer.captureSnapshot()
207
- const cmd = createSnapshotCommand(layer, before, after)
208
- 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.
209
246
 
210
- // Layer commands
211
- import { CreateLayerCommand, DeleteLayerCommand } from '@lancercomet/zoom-pan'
247
+ ```ts
248
+ view.hasPlugin('bounds')
249
+ view.listPlugins()
250
+ view.clearPlugins()
212
251
 
213
- history.undo()
214
- history.redo()
215
- history.canUndo()
216
- history.canRedo()
252
+ view.unuse('bounds')
217
253
  ```
218
254
 
219
255
  ## Examples
220
256
 
221
- See `examples/` folder:
257
+ See `examples/`:
222
258
 
223
- - **`examples/viewer/`** - Simple image viewer
224
- - **`examples/painter/`** - Full drawing app with brush, eraser, layers, undo/redo
225
- - **`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 };