@@ -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,7 +333,7 @@ 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
@@ -242,7 +343,10 @@ export class CanvasManager implements CanvasManagerInterface {
242
343
if ( canvas . width === 0 || canvas . height === 0 ) return ;
243
344
244
345
snapshotInProgressMap . set ( id , true ) ;
245
- if ( [ 'webgl' , 'webgl2' ] . includes ( ( canvas as ICanvas ) . __context ) ) {
346
+ if (
347
+ ! isManualSnapshot &&
348
+ [ 'webgl' , 'webgl2' ] . includes ( ( canvas as ICanvas ) . __context )
349
+ ) {
246
350
// if the canvas hasn't been modified recently,
247
351
// its contents won't be in memory and `createImageBitmap`
248
352
// will return a transparent imageBitmap
@@ -273,7 +377,7 @@ export class CanvasManager implements CanvasManagerInterface {
273
377
bitmap,
274
378
width : canvas . width ,
275
379
height : canvas . height ,
276
- dataURLOptions : options . dataURLOptions ,
380
+ dataURLOptions,
277
381
} ,
278
382
[ bitmap ] ,
279
383
) ;
@@ -288,51 +392,7 @@ export class CanvasManager implements CanvasManagerInterface {
288
392
} ;
289
393
290
394
rafId = onRequestAnimationFrame ( takeCanvasSnapshots ) ;
291
-
292
- this . resetObservers = ( ) => {
293
- canvasContextReset ( ) ;
294
- cancelAnimationFrame ( rafId ) ;
295
- } ;
296
- }
297
-
298
- private initCanvasMutationObserver (
299
- win : IWindow ,
300
- blockClass : blockClass ,
301
- blockSelector : string | null ,
302
- unblockSelector : string | null ,
303
- ) : void {
304
- this . startRAFTimestamping ( ) ;
305
- this . startPendingCanvasMutationFlusher ( ) ;
306
-
307
- const canvasContextReset = initCanvasContextObserver (
308
- win ,
309
- blockClass ,
310
- blockSelector ,
311
- unblockSelector ,
312
- false ,
313
- ) ;
314
- const canvas2DReset = initCanvas2DMutationObserver (
315
- this . processMutation . bind ( this ) ,
316
- win ,
317
- blockClass ,
318
- blockSelector ,
319
- unblockSelector ,
320
- ) ;
321
-
322
- const canvasWebGL1and2Reset = initCanvasWebGLMutationObserver (
323
- this . processMutation . bind ( this ) ,
324
- win ,
325
- blockClass ,
326
- blockSelector ,
327
- unblockSelector ,
328
- this . mirror ,
329
- ) ;
330
-
331
- this . resetObservers = ( ) => {
332
- canvasContextReset ( ) ;
333
- canvas2DReset ( ) ;
334
- canvasWebGL1and2Reset ( ) ;
335
- } ;
395
+ return rafId ;
336
396
}
337
397
338
398
private startPendingCanvasMutationFlusher ( ) {
0 commit comments