1
+ import type { SlidevContextNavFull } from '../composables/useNav'
2
+ import type { ScreenshotSession } from './screenshot'
3
+ import { sleep } from '@antfu/utils'
4
+ import { slideHeight , slideWidth } from '../env'
5
+ import { captureDelay } from '../state'
1
6
import { snapshotState } from '../state/snapshot'
7
+ import { isDark } from './dark'
8
+ import { disableTransition } from './hmr'
9
+ import { startScreenshotSession } from './screenshot'
2
10
import { getSlide } from './slides'
3
11
12
+ const chromeVersion = window . navigator . userAgent . match ( / C h r o m e \/ ( \d + ) / ) ?. [ 1 ]
13
+ export const isScreenshotSupported = chromeVersion ? Number ( chromeVersion ) >= 94 : false
14
+
15
+ const initialWait = 100
16
+
4
17
export class SlideSnapshotManager {
5
- private _capturePromises = new Map < number , Promise < void > > ( )
18
+ private _screenshotSession : ScreenshotSession | null = null
6
19
7
- getSnapshot ( slideNo : number ) {
8
- const data = snapshotState . state [ slideNo ]
20
+ getSnapshot ( slideNo : number , isDark : boolean ) {
21
+ const id = slideNo + ( isDark ? '-dark' : '-light' )
22
+ const data = snapshotState . state [ id ]
9
23
if ( ! data ) {
10
24
return
11
25
}
@@ -18,67 +32,75 @@ export class SlideSnapshotManager {
18
32
}
19
33
}
20
34
21
- async captureSnapshot ( slideNo : number , el : HTMLElement , delay = 1000 ) {
35
+ private async saveSnapshot ( slideNo : number , dataUrl : string , isDark : boolean ) {
22
36
if ( ! __DEV__ )
23
- return
24
- if ( this . getSnapshot ( slideNo ) ) {
25
- return
26
- }
27
- if ( this . _capturePromises . has ( slideNo ) ) {
28
- await this . _capturePromises . get ( slideNo )
29
- }
30
- const promise = this . _captureSnapshot ( slideNo , el , delay )
31
- . finally ( ( ) => {
32
- this . _capturePromises . delete ( slideNo )
33
- } )
34
- this . _capturePromises . set ( slideNo , promise )
35
- await promise
36
- }
37
-
38
- private async _captureSnapshot ( slideNo : number , el : HTMLElement , delay : number ) {
39
- if ( ! __DEV__ )
40
- return
37
+ return false
41
38
const slide = getSlide ( slideNo )
42
39
if ( ! slide )
43
- return
40
+ return false
44
41
42
+ const id = slideNo + ( isDark ? '-dark' : '-light' )
45
43
const revision = slide . meta . slide . revision
44
+ snapshotState . patch ( id , {
45
+ revision,
46
+ image : dataUrl ,
47
+ } )
48
+ }
46
49
47
- // Retry until the slide is loaded
48
- let retries = 100
49
- while ( retries -- > 0 ) {
50
- if ( ! el . querySelector ( '.slidev-slide-loading' ) )
51
- break
52
- await new Promise ( r => setTimeout ( r , 100 ) )
53
- }
50
+ async startCapturing ( nav : SlidevContextNavFull ) {
51
+ if ( ! __DEV__ )
52
+ return false
54
53
55
- // Artificial delay for the content to be loaded
56
- await new Promise ( r => setTimeout ( r , delay ) )
54
+ // TODO: show a dialog to confirm
55
+
56
+ if ( this . _screenshotSession ) {
57
+ this . _screenshotSession . dispose ( )
58
+ this . _screenshotSession = null
59
+ }
57
60
58
- // Capture the snapshot
59
- const toImage = await import ( 'html-to-image' )
60
61
try {
61
- const dataUrl = await toImage . toPng ( el , {
62
- width : el . offsetWidth ,
63
- height : el . offsetHeight ,
64
- skipFonts : true ,
65
- cacheBust : true ,
66
- pixelRatio : 1.5 ,
67
- } )
68
- if ( revision !== slide . meta . slide . revision ) {
69
- // eslint-disable-next-line no-console
70
- console . info ( '[Slidev] Slide' , slideNo , 'changed, discarding the snapshot' )
71
- return
62
+ this . _screenshotSession = await startScreenshotSession (
63
+ slideWidth . value ,
64
+ slideHeight . value ,
65
+ )
66
+
67
+ disableTransition . value = true
68
+ nav . go ( 1 , 0 , true )
69
+
70
+ await sleep ( initialWait + captureDelay . value )
71
+ while ( true ) {
72
+ if ( ! this . _screenshotSession ) {
73
+ break
74
+ }
75
+ this . saveSnapshot (
76
+ nav . currentSlideNo . value ,
77
+ this . _screenshotSession . screenshot ( document . getElementById ( 'slide-content' ) ! ) ,
78
+ isDark . value ,
79
+ )
80
+ if ( nav . hasNext . value ) {
81
+ await sleep ( captureDelay . value )
82
+ nav . nextSlide ( true )
83
+ await sleep ( captureDelay . value )
84
+ }
85
+ else {
86
+ break
87
+ }
72
88
}
73
- snapshotState . patch ( slideNo , {
74
- revision,
75
- image : dataUrl ,
76
- } )
77
- // eslint-disable-next-line no-console
78
- console . info ( '[Slidev] Snapshot captured for slide' , slideNo )
89
+
90
+ // TODO: show a message when done
91
+
92
+ return true
79
93
}
80
94
catch ( e ) {
81
- console . error ( '[Slidev] Failed to capture snapshot for slide' , slideNo , e )
95
+ console . error ( e )
96
+ return false
97
+ }
98
+ finally {
99
+ disableTransition . value = false
100
+ if ( this . _screenshotSession ) {
101
+ this . _screenshotSession . dispose ( )
102
+ this . _screenshotSession = null
103
+ }
82
104
}
83
105
}
84
106
}
0 commit comments