Предыдущая статья《Создайте инструмент скриншота с помощью электрона с нуля«После релиза я нашел много, но инструмент действительно использовался, поэтому, чтобы сделать наши инструменты для скриншотов лучше, я сделал много оптимизации, и, конечно же, я столкнулся с множеством ям.
Рендеринг скриншотов:
Измененный полный код проекта по-прежнему имеет прежний адрес:GitHub.com/Крис и/о…Приветствую всех, чтобы обратить внимание
Далее мы перечислим проблемы и конкретные меры, которые необходимо решить.
1. Скриншот одна мгновенная картонная проблема
Сначала поместите код скриншота предыдущей версии
console.time('capture')
desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: {
width: width * scaleFactor,
height: height * scaleFactor,
}
}, (error, sources) => {
console.timeEnd('capture')
let imgSrc = sources[0].thumbnail.toDataURL()
let capture = new CaptureRenderer($canvas, $bg, imgSrc, scaleFactor)
})
desktopCapturer.getSources
Приводит к зависанию всей программы, а время зависания зависит от разрешения экрана, количества экранов и производительности компьютера.
В случае самостоятельного использования Macbook Pro с внешним монитором 2K скриншот может зависнуть более чем на 2 секунды, а мышь также будет отображаться в стиле ожидания.Этот опыт довольно плохой.
Так что надо искать альтернативы, см.GitHub.com/электрон/плохой…иGitHub.com/электрон/плохой…Для этих двух проблем есть две альтернативы: первый — использовать некоторые сторонние собственные программы для создания скриншотов, а второй — использоватьgetUserMedia
Я выбрал второй способ, в основном потому, что он прост. Вы можете попробовать первый способ, отзывы приветствуются.
Модифицированный код прикреплен ниже
const handleStream = (stream) => {
document.body.style.cursor = oldCursor
document.body.style.opacity = '1'
// Create hidden video tag
let video = document.createElement('video')
video.style.cssText = 'position:absolute;top:-10000px;left:-10000px;'
// Event connected to stream
let loaded = false
video.onloadedmetadata = () => {
if (loaded) {
return
}
loaded = true
// Set video ORIGINAL height (screenshot)
video.style.height = video.videoHeight + 'px' // videoHeight
video.style.width = video.videoWidth + 'px' // videoWidth
// Create canvas
let canvas = document.createElement('canvas')
canvas.width = video.videoWidth
canvas.height = video.videoHeight
let ctx = canvas.getContext('2d')
// Draw video on canvas
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
if (this.callback) {
// Save screenshot to png - base64
this.callback(canvas.toDataURL('image/png'))
} else {
// console.log('Need callback!')
}
// Remove hidden video tag
video.remove()
try {
stream.getTracks()[0].stop()
} catch (e) {
// nothing
}
}
video.srcObject = stream
document.body.appendChild(video)
}
// mac 和 windows 获取 chromeMediaSourceId 的方式不同
if (require('os').platform() === 'win32') {
require('electron').desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: { width: 1, height: 1 },
}, (e, sources) => {
let selectSource = sources.filter(source => source.display_id + '' === curScreen.id + '')[0]
navigator.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: selectSource.id + '',
minWidth: 1280,
minHeight: 720,
maxWidth: 8000,
maxHeight: 8000,
},
},
}, handleStream, handleError)
})
} else {
navigator.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: `screen:${curScreen.id}`,
minWidth: 1280,
minHeight: 720,
maxWidth: 8000,
maxHeight: 8000,
},
},
}, handleStream, handleError)
}
Код немного больше, в основном скопирован. Его принцип заключается в использованииgetUserMedia
Чтобы записать экран, получите видеоресурс, затем нарисуйте видео на холсте и, наконец, преобразуйте его в URL-адрес.
После модификации захват экрана не будет вызывать зависания всей программы, а время сокращается примерно до 600мс, что уже приемлемо для захвата экрана.
2. Поддержка нескольких экранов
Когда на компьютере несколько мониторов, многоэкранные скриншоты очень важны Я только что упомянул случай с одним экраном, так что мне делать с несколькими экранами?
Из-за полноэкранной ситуации окно может занимать только один экран, поэтому многоэкранные снимки экрана могут обрабатываться только с несколькими окнами снимков экрана (в Windows может быть способ сделать полноэкранное отображение окна по всему экрану, чтобы попробовать)
Чтобы сначала создать окно, вам нужно сначала получить количество экранов и создать цикл.
const captureScreen = (e, args) => {
if (captureWins.length) {
return
}
const { screen } = require('electron')
let displays = screen.getAllDisplays()
// 循环创建截屏窗口
captureWins = displays.map((display) => {
let captureWin = new BrowserWindow({
// window 使用 fullscreen, mac 设置为 undefined, 不可为 false
fullscreen: os.platform() === 'win32' || undefined,
width: display.bounds.width,
height: display.bounds.height,
x: display.bounds.x,
y: display.bounds.y,
transparent: true,
frame: false,
movable: false,
resizable: false,
enableLargerThanScreen: true,
hasShadow: false,
})
captureWin.setAlwaysOnTop(true, 'screen-saver')
captureWin.setFullScreenable(false)
captureWin.loadFile(path.join(__dirname, 'capture.html'))
// 调试用
// captureWin.openDevTools()
// 一个窗口关闭则关闭所有窗口
captureWin.on('closed', () => {
let index = captureWins.indexOf(captureWin)
if (index !== -1) {
captureWins.splice(index, 1)
}
captureWins.forEach(win => win.close())
})
return captureWin
})
}
Затем каждое окно делает скриншот текущего экрана для работы, а текущий экран можно получить следующим образом
// 因为窗口是全屏的, 所以可以直接用 x, y 来对比
const getCurrentScreen = () => {
let { x, y } = currentWindow.getBounds()
return screen.getAllDisplays().filter(d => d.bounds.x === x && d.bounds.y === y)[0]
}
Затем по коду скриншота вопроса 1 можно получить скриншот текущего экрана, среди которыхchromeMediaSourceId
Представляет идентификатор экрана
Изменено здесь, в основном то же самое, но есть небольшая проблема, потому что есть несколько окон, каждое окно можно выбрать, перетащив область изображения. Обратитесь к практике QQ на Mac, когда на одном экране есть выбор, на другом экране операция запрещена
Многооконный интерфейс совместим, используется связь IPC. После того, как выбор окна отправлен в основной процесс, основной процесс транслируется в другие окна, а другое окно после получения запрещается.
// main 进程
ipcMain.on('capture-screen', (e, { type = 'start', screenId, url } = {}) => {
// ...
if (type === 'select') {
captureWins.forEach(win => win.webContents.send('capture-screen', { type: 'select', screenId }))
}
})
// renderer 进程
ipcRenderer.on('capture-screen', (e, { type, screenId }) => {
if (type === 'select') {
if (screenId && screenId !== currentScreen.id) {
capture.disable()
}
}
})
3. Захват полноэкранного окна под Mac
Для отображения окна поверх полноэкранного окна под Mac требуется волшебный код.Конечно,код пишется через поиск,но конкретика не очень понятна.Похоже на какой-то метод взлома.
В моем случае я могу назвать это только «черной магией».
Следующий код помещается после кода, который создает окно скриншота
let captureWin = new BrowserWindow({
// window 使用 fullscreen, mac 设置为 undefined, 不可为 false
fullscreen: os.platform() === 'win32' || undefined,
width: display.bounds.width,
height: display.bounds.height,
x: display.bounds.x,
y: display.bounds.y,
transparent: true,
frame: false,
movable: false,
resizable: false,
enableLargerThanScreen: true,
hasShadow: false,
show: false,
})
// 黑魔法...
app.dock.hide()
captureWin.setAlwaysOnTop(true, 'screen-saver')
captureWin.setVisibleOnAllWorkspaces(true)
captureWin.setFullScreenable(false)
captureWin.show()
app.dock.show()
captureWin.setVisibleOnAllWorkspaces(false)
После вышеуказанной оптимизации этот инструмент для создания снимков экрана может выйти на производственный уровень. Конечно, есть еще некоторые недостатки, такие как кросс-экранные скриншоты, граффити и различные детали опыта, у меня будет время, чтобы оптимизировать это позже, и тогда я поделюсь этим с вами! ! !