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'
16import { snapshotState } from '../state/snapshot'
7+ import { isDark } from './dark'
8+ import { disableTransition } from './hmr'
9+ import { startScreenshotSession } from './screenshot'
210import { getSlide } from './slides'
311
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+
417export class SlideSnapshotManager {
5- private _capturePromises = new Map < number , Promise < void > > ( )
18+ private _screenshotSession : ScreenshotSession | null = null
619
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 ]
923 if ( ! data ) {
1024 return
1125 }
@@ -18,67 +32,75 @@ export class SlideSnapshotManager {
1832 }
1933 }
2034
21- async captureSnapshot ( slideNo : number , el : HTMLElement , delay = 1000 ) {
35+ private async saveSnapshot ( slideNo : number , dataUrl : string , isDark : boolean ) {
2236 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
4138 const slide = getSlide ( slideNo )
4239 if ( ! slide )
43- return
40+ return false
4441
42+ const id = slideNo + ( isDark ? '-dark' : '-light' )
4543 const revision = slide . meta . slide . revision
44+ snapshotState . patch ( id , {
45+ revision,
46+ image : dataUrl ,
47+ } )
48+ }
4649
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
5453
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+ }
5760
58- // Capture the snapshot
59- const toImage = await import ( 'html-to-image' )
6061 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+ }
7288 }
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
7993 }
8094 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+ }
82104 }
83105 }
84106}
0 commit comments