返回 slidev
screenshot.ts
根目录 / packages / client / logic / screenshot.ts
1 import { computed, ref } from 'vue'
2
3 export async function startScreenshotSession(width: number, height: number) {
4 const canvas = document.createElement('canvas')
5 canvas.width = width
6 canvas.height = height
7 const context = canvas.getContext('2d')!
8 const video = document.createElement('video')
9 video.width = width
10 video.height = height
11
12 const captureStream = ref<MediaStream | null>(await navigator.mediaDevices.getDisplayMedia({
13 video: {
14 // Use a rather small frame rate
15 frameRate: 10,
16 // @ts-expect-error missing types
17 cursor: 'never',
18 },
19 selfBrowserSurface: 'include',
20 preferCurrentTab: true,
21 }))
22 captureStream.value!.addEventListener('inactive', dispose)
23
24 video.srcObject = captureStream.value!
25 video.play()
26
27 function screenshot(element: HTMLElement) {
28 if (!captureStream.value)
29 throw new Error('captureStream inactive')
30 context.clearRect(0, 0, width, height)
31 const { left, top, width: elWidth } = element.getBoundingClientRect()
32 context.drawImage(
33 video,
34 left * window.devicePixelRatio,
35 top * window.devicePixelRatio,
36 elWidth * window.devicePixelRatio,
37 elWidth / width * height * window.devicePixelRatio,
38 0,
39 0,
40 width,
41 height,
42 )
43 return canvas.toDataURL('image/png')
44 }
45
46 function dispose() {
47 captureStream.value?.getTracks().forEach(track => track.stop())
48 captureStream.value = null
49 }
50
51 return {
52 isActive: computed(() => !!captureStream.value),
53 screenshot,
54 dispose,
55 }
56 }
57
58 export type ScreenshotSession = Awaited<ReturnType<typeof startScreenshotSession>>
59
60 const chromeVersion = window.navigator.userAgent.match(/Chrome\/(\d+)/)?.[1]
61 export const isScreenshotSupported = chromeVersion ? Number(chromeVersion) >= 94 : false
62
62 lines TYPESCRIPT