1
+ import type { SlidevContextNavFull } from '../composables/useNav'
2
+ import type { ScreenshotSession } from './screenshot'
3
+ import { sleep } from '@antfu/utils'
4
+ import { useLocalStorage } from '@vueuse/core'
5
+ import { slideHeight , slideWidth } from '../env'
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
+ const delay = useLocalStorage ( 'slidev-export-capture-delay' , 400 , { listenToStorageChanges : false } )
17
+
4
18
export class SlideSnapshotManager {
5
- private _capturePromises = new Map < number , Promise < void > > ( )
19
+ private _screenshotSession : ScreenshotSession | null = null
6
20
7
- getSnapshot ( slideNo : number ) {
8
- const data = snapshotState . state [ slideNo ]
21
+ getSnapshot ( slideNo : number , isDark : boolean ) {
22
+ const id = slideNo + ( isDark ? '-dark' : '-light' )
23
+ const data = snapshotState . state [ id ]
9
24
if ( ! data ) {
10
25
return
11
26
}
@@ -18,67 +33,76 @@ export class SlideSnapshotManager {
18
33
}
19
34
}
20
35
21
- async captureSnapshot ( slideNo : number , el : HTMLElement , delay = 1000 ) {
36
+ private async saveSnapshot ( slideNo : number , dataUrl : string , isDark : boolean ) {
22
37
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
38
+ return false
41
39
const slide = getSlide ( slideNo )
42
40
if ( ! slide )
43
- return
41
+ return false
44
42
43
+ const id = slideNo + ( isDark ? '-dark' : '-light' )
45
44
const revision = slide . meta . slide . revision
45
+ snapshotState . patch ( id , {
46
+ revision,
47
+ image : dataUrl ,
48
+ } )
49
+ }
46
50
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
- }
51
+ async startCapturing ( nav : SlidevContextNavFull ) {
52
+ if ( ! __DEV__ )
53
+ return false
54
54
55
- // Artificial delay for the content to be loaded
56
- await new Promise ( r => setTimeout ( r , delay ) )
55
+ // TODO: show a dialog to confirm
56
+
57
+ if ( this . _screenshotSession ) {
58
+ this . _screenshotSession . dispose ( )
59
+ this . _screenshotSession = null
60
+ }
57
61
58
- // Capture the snapshot
59
- const toImage = await import ( 'html-to-image' )
60
62
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
63
+ const scale = 2
64
+ this . _screenshotSession = await startScreenshotSession (
65
+ slideWidth . value * scale ,
66
+ slideHeight . value * scale ,
67
+ )
68
+
69
+ disableTransition . value = true
70
+ nav . go ( 1 , 0 , true )
71
+
72
+ await sleep ( initialWait + delay . value )
73
+ while ( true ) {
74
+ if ( ! this . _screenshotSession ) {
75
+ break
76
+ }
77
+ this . saveSnapshot (
78
+ nav . currentSlideNo . value ,
79
+ this . _screenshotSession . screenshot ( document . getElementById ( 'slide-content' ) ! ) ,
80
+ isDark . value ,
81
+ )
82
+ if ( nav . hasNext . value ) {
83
+ await sleep ( delay . value )
84
+ nav . nextSlide ( true )
85
+ await sleep ( delay . value )
86
+ }
87
+ else {
88
+ break
89
+ }
72
90
}
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 )
91
+
92
+ // TODO: show a message when done
93
+
94
+ return true
79
95
}
80
96
catch ( e ) {
81
- console . error ( '[Slidev] Failed to capture snapshot for slide' , slideNo , e )
97
+ console . error ( e )
98
+ return false
99
+ }
100
+ finally {
101
+ disableTransition . value = false
102
+ if ( this . _screenshotSession ) {
103
+ this . _screenshotSession . dispose ( )
104
+ this . _screenshotSession = null
105
+ }
82
106
}
83
107
}
84
108
}
0 commit comments