Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/electron-screenshots/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Lang {
operation_arrow_title?: string;
operation_ellipse_title?: string;
operation_rectangle_title?: string;
operation_long_title?: string;
}

export interface ScreenshotsOpts {
Expand All @@ -57,6 +58,8 @@ export default class Screenshots extends Events {

private singleWindow: boolean;

private display: Display | null = null;

private isReady = new Promise<void>((resolve) => {
ipcMain.once('SCREENSHOTS:ready', () => {
this.logger('SCREENSHOTS:ready');
Expand Down Expand Up @@ -85,6 +88,7 @@ export default class Screenshots extends Events {
this.logger('startCapture');

const display = getDisplay();
this.display = display;

const [imageUrl] = await Promise.all([this.capture(display), this.isReady]);

Expand Down Expand Up @@ -396,5 +400,20 @@ export default class Screenshots extends Events {
this.endCapture();
},
);

ipcMain.handle('SCREENSHOTS:startLong', () => {
this.$win?.setIgnoreMouseEvents(true, { forward: true });
});

ipcMain.handle('SCREENSHOTS:stopLong', () => {
this.$win?.setIgnoreMouseEvents(false);
});

ipcMain.handle('SCREENSHOTS:captureLong', async () => {
if (!this.display) {
return null;
}
return this.capture(this.display);
});
}
}
9 changes: 9 additions & 0 deletions packages/electron-screenshots/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ contextBridge.exposeInMainWorld('screenshots', {

ipcRenderer.send('SCREENSHOTS:cancel');
},
startLong: () => {
ipcRenderer.invoke('SCREENSHOTS:startLong');
},
stopLong: () => {
ipcRenderer.invoke('SCREENSHOTS:stopLong');
},
captureLong: () => {
return ipcRenderer.invoke('SCREENSHOTS:captureLong');
},
ok: (arrayBuffer: ArrayBuffer, data: ScreenshotsData) => {
console.log('contextBridge ok', arrayBuffer, data);

Expand Down
100 changes: 100 additions & 0 deletions packages/react-screenshots/src/Screenshots/operations/Long/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
import ScreenshotsButton from '../../ScreenshotsButton'
import useStore from '../../hooks/useStore'
import useCall from '../../hooks/useCall'
import useReset from '../../hooks/useReset'

export default function Long (): ReactElement {
const { bounds, lang } = useStore()
const call = useCall()
const reset = useReset()
const [capturing, setCapturing] = useState(false)
const [segments, setSegments] = useState<string[]>([])

const capture = useCallback(async () => {
const url = (await window.screenshots.captureLong()) as string | null
if (url) {
setSegments((arr) => [...arr, url])
}
}, [])

useEffect(() => {
if (!capturing) return
const id = window.setInterval(capture, 500)
return () => window.clearInterval(id)
}, [capturing, capture])

const start = useCallback(async () => {
setSegments([])
await window.screenshots.startLong()
await capture()
setCapturing(true)
}, [capture])

const stop = useCallback(async () => {
await capture()
await window.screenshots.stopLong()
if (!bounds) {
setCapturing(false)
setSegments([])
return
}
const images = await Promise.all(
segments.map(
(u) =>
new Promise<HTMLImageElement>((resolve, reject) => {
const img = new Image()
img.onload = () => resolve(img)
img.onerror = reject
img.src = u
})
)
)
const canvas = document.createElement('canvas')
canvas.width = bounds.width
canvas.height = bounds.height * images.length
const ctx = canvas.getContext('2d')
if (ctx) {
images.forEach((img, i) => {
ctx.drawImage(
img,
bounds.x,
bounds.y,
bounds.width,
bounds.height,
0,
i * bounds.height,
bounds.width,
bounds.height
)
})
}
const blob: Blob | null = await new Promise((res) =>
canvas.toBlob((b) => res(b), 'image/png')
)
if (blob) {
const totalBounds = { ...bounds, height: bounds.height * images.length }
call('onOk', blob, totalBounds)
reset()
}
setCapturing(false)
setSegments([])
}, [bounds, capture, call, reset, segments])

const onClick = useCallback(() => {
if (capturing) {
stop()
} else {
start()
}
}, [capturing, start, stop])

return (
<ScreenshotsButton
title={lang.operation_long_title || '长截图'}
icon='icon-rectangle'
checked={capturing}
onClick={onClick}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import Brush from './Brush'
import Arrow from './Arrow'
import Ellipse from './Ellipse'
import Rectangle from './Rectangle'
import Long from './Long'

export default [Rectangle, Ellipse, Arrow, Brush, Text, Mosaic, '|', Undo, Redo, '|', Save, Cancel, Ok]
export default [Rectangle, Ellipse, Arrow, Brush, Text, Mosaic, '|', Undo, Redo, '|', Long, Save, Cancel, Ok]
4 changes: 3 additions & 1 deletion packages/react-screenshots/src/Screenshots/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface Lang {
operation_arrow_title: string
operation_ellipse_title: string
operation_rectangle_title: string
operation_long_title: string
}

const zhCN: Lang = {
Expand All @@ -25,7 +26,8 @@ const zhCN: Lang = {
operation_brush_title: '画笔',
operation_arrow_title: '箭头',
operation_ellipse_title: '椭圆',
operation_rectangle_title: '矩形'
operation_rectangle_title: '矩形',
operation_long_title: '长截图'
}

export default zhCN