@@ -35,10 +35,12 @@ export interface CanvasManagerInterface {
35
35
unfreeze ( ) : void ;
36
36
lock ( ) : void ;
37
37
unlock ( ) : void ;
38
+ snapshot ( canvasElement ?: HTMLCanvasElement ) : void ;
38
39
}
39
40
40
41
export interface CanvasManagerConstructorOptions {
41
42
recordCanvas : boolean ;
43
+ isManualSnapshot ?: boolean ;
42
44
mutationCb : canvasMutationCallback ;
43
45
win : IWindow ;
44
46
blockClass : blockClass ;
@@ -65,11 +67,15 @@ export class CanvasManagerNoop implements CanvasManagerInterface {
65
67
public unlock ( ) {
66
68
// noop
67
69
}
70
+ public snapshot ( ) {
71
+ // noop
72
+ }
68
73
}
69
74
70
75
export class CanvasManager implements CanvasManagerInterface {
71
76
private pendingCanvasMutations : pendingCanvasMutationsMap = new Map ( ) ;
72
77
private rafStamps : RafStamps = { latestId : 0 , invokeId : null } ;
78
+ private options : CanvasManagerConstructorOptions ;
73
79
private mirror : Mirror ;
74
80
75
81
private mutationCb : canvasMutationCallback ;
@@ -110,6 +116,11 @@ export class CanvasManager implements CanvasManagerInterface {
110
116
} = options ;
111
117
this . mutationCb = options . mutationCb ;
112
118
this . mirror = options . mirror ;
119
+ this . options = options ;
120
+
121
+ if ( options . isManualSnapshot ) {
122
+ return ;
123
+ }
113
124
114
125
callbackWrapper ( ( ) => {
115
126
if ( recordCanvas && sampling === 'all' )
@@ -167,6 +178,90 @@ export class CanvasManager implements CanvasManagerInterface {
167
178
unblockSelector ,
168
179
true ,
169
180
) ;
181
+ const rafId = this . takeSnapshot (
182
+ false ,
183
+ fps ,
184
+ win ,
185
+ blockClass ,
186
+ blockSelector ,
187
+ unblockSelector ,
188
+ options . dataURLOptions ,
189
+ ) ;
190
+
191
+ this . resetObservers = ( ) => {
192
+ canvasContextReset ( ) ;
193
+ cancelAnimationFrame ( rafId ) ;
194
+ } ;
195
+ }
196
+
197
+ private initCanvasMutationObserver (
198
+ win : IWindow ,
199
+ blockClass : blockClass ,
200
+ blockSelector : string | null ,
201
+ unblockSelector : string | null ,
202
+ ) : void {
203
+ this . startRAFTimestamping ( ) ;
204
+ this . startPendingCanvasMutationFlusher ( ) ;
205
+
206
+ const canvasContextReset = initCanvasContextObserver (
207
+ win ,
208
+ blockClass ,
209
+ blockSelector ,
210
+ unblockSelector ,
211
+ false ,
212
+ ) ;
213
+ const canvas2DReset = initCanvas2DMutationObserver (
214
+ this . processMutation . bind ( this ) ,
215
+ win ,
216
+ blockClass ,
217
+ blockSelector ,
218
+ unblockSelector ,
219
+ ) ;
220
+
221
+ const canvasWebGL1and2Reset = initCanvasWebGLMutationObserver (
222
+ this . processMutation . bind ( this ) ,
223
+ win ,
224
+ blockClass ,
225
+ blockSelector ,
226
+ unblockSelector ,
227
+ this . mirror ,
228
+ ) ;
229
+
230
+ this . resetObservers = ( ) => {
231
+ canvasContextReset ( ) ;
232
+ canvas2DReset ( ) ;
233
+ canvasWebGL1and2Reset ( ) ;
234
+ } ;
235
+ }
236
+
237
+ public snapshot ( canvasElement ?: HTMLCanvasElement ) {
238
+ const { options } = this ;
239
+ const rafId = this . takeSnapshot (
240
+ true ,
241
+ options . sampling === 'all' ? 2 : options . sampling || 2 ,
242
+ options . win ,
243
+ options . blockClass ,
244
+ options . blockSelector ,
245
+ options . unblockSelector ,
246
+ options . dataURLOptions ,
247
+ canvasElement ,
248
+ ) ;
249
+
250
+ this . resetObservers = ( ) => {
251
+ cancelAnimationFrame ( rafId ) ;
252
+ } ;
253
+ }
254
+
255
+ private takeSnapshot (
256
+ isManualSnapshot : boolean ,
257
+ fps : number ,
258
+ win : IWindow ,
259
+ blockClass : blockClass ,
260
+ blockSelector : string | null ,
261
+ unblockSelector : string | null ,
262
+ dataURLOptions : DataURLOptions ,
263
+ canvasElement ?: HTMLCanvasElement ,
264
+ ) {
170
265
const snapshotInProgressMap : Map < number , boolean > = new Map ( ) ;
171
266
const worker = new Worker ( getImageBitmapDataUrlWorkerURL ( ) ) ;
172
267
worker . onmessage = ( e ) => {
@@ -210,7 +305,13 @@ export class CanvasManager implements CanvasManagerInterface {
210
305
let lastSnapshotTime = 0 ;
211
306
let rafId : number ;
212
307
213
- const getCanvas = ( ) : HTMLCanvasElement [ ] => {
308
+ const getCanvas = (
309
+ canvasElement ?: HTMLCanvasElement ,
310
+ ) : HTMLCanvasElement [ ] => {
311
+ if ( canvasElement ) {
312
+ return [ canvasElement ] ;
313
+ }
314
+
214
315
const matchedCanvas : HTMLCanvasElement [ ] = [ ] ;
215
316
win . document . querySelectorAll ( 'canvas' ) . forEach ( ( canvas ) => {
216
317
if (
@@ -232,11 +333,14 @@ export class CanvasManager implements CanvasManagerInterface {
232
333
}
233
334
lastSnapshotTime = timestamp ;
234
335
235
- getCanvas ( ) . forEach ( ( canvas : HTMLCanvasElement ) => {
336
+ getCanvas ( canvasElement ) . forEach ( ( canvas : HTMLCanvasElement ) => {
236
337
const id = this . mirror . getId ( canvas ) ;
237
338
if ( snapshotInProgressMap . get ( id ) ) return ;
238
339
snapshotInProgressMap . set ( id , true ) ;
239
- if ( [ 'webgl' , 'webgl2' ] . includes ( ( canvas as ICanvas ) . __context ) ) {
340
+ if (
341
+ ! isManualSnapshot &&
342
+ [ 'webgl' , 'webgl2' ] . includes ( ( canvas as ICanvas ) . __context )
343
+ ) {
240
344
// if the canvas hasn't been modified recently,
241
345
// its contents won't be in memory and `createImageBitmap`
242
346
// will return a transparent imageBitmap
@@ -267,7 +371,7 @@ export class CanvasManager implements CanvasManagerInterface {
267
371
bitmap,
268
372
width : canvas . width ,
269
373
height : canvas . height ,
270
- dataURLOptions : options . dataURLOptions ,
374
+ dataURLOptions,
271
375
} ,
272
376
[ bitmap ] ,
273
377
) ;
@@ -282,51 +386,7 @@ export class CanvasManager implements CanvasManagerInterface {
282
386
} ;
283
387
284
388
rafId = onRequestAnimationFrame ( takeCanvasSnapshots ) ;
285
-
286
- this . resetObservers = ( ) => {
287
- canvasContextReset ( ) ;
288
- cancelAnimationFrame ( rafId ) ;
289
- } ;
290
- }
291
-
292
- private initCanvasMutationObserver (
293
- win : IWindow ,
294
- blockClass : blockClass ,
295
- blockSelector : string | null ,
296
- unblockSelector : string | null ,
297
- ) : void {
298
- this . startRAFTimestamping ( ) ;
299
- this . startPendingCanvasMutationFlusher ( ) ;
300
-
301
- const canvasContextReset = initCanvasContextObserver (
302
- win ,
303
- blockClass ,
304
- blockSelector ,
305
- unblockSelector ,
306
- false ,
307
- ) ;
308
- const canvas2DReset = initCanvas2DMutationObserver (
309
- this . processMutation . bind ( this ) ,
310
- win ,
311
- blockClass ,
312
- blockSelector ,
313
- unblockSelector ,
314
- ) ;
315
-
316
- const canvasWebGL1and2Reset = initCanvasWebGLMutationObserver (
317
- this . processMutation . bind ( this ) ,
318
- win ,
319
- blockClass ,
320
- blockSelector ,
321
- unblockSelector ,
322
- this . mirror ,
323
- ) ;
324
-
325
- this . resetObservers = ( ) => {
326
- canvasContextReset ( ) ;
327
- canvas2DReset ( ) ;
328
- canvasWebGL1and2Reset ( ) ;
329
- } ;
389
+ return rafId ;
330
390
}
331
391
332
392
private startPendingCanvasMutationFlusher ( ) {
0 commit comments