内容简介:上一篇文章《项目修改后的完整代码依然是之前的地址:接下来就列举一下解决的问题和具体做法
上一篇文章《 从零开始用 electron 手撸一个截屏工具 》发布之后发现阅读的朋友还不少,不过 工具 真正使用的时候就发现了问题,所以为了让我们的截图工具更好用,就又做了很多优化,当然了也遇到了很多坑。
项目修改后的完整代码依然是之前的地址: github.com/chrisbing/e… 欢迎大家关注
接下来就列举一下解决的问题和具体做法
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/electron/el…
和 github.com/electron/el…
这两个 Issue,替代方案有两种,第一种用第三方原生的一些截屏程序,第二种是利用 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
来录屏,获取到视频资源,然后将视频绘制到 canvas 上,最后转换成 url。
修改后截屏不会出现整个程序挂起的情况,时间也缩小到600ms 左右,这个时间对于截图来说已经是可以接受的了。
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
代表的就是屏幕的 ID
改到这里,大体上就差不多了,但是还有个小问题,因为是多个窗口,每个窗口都可以通过拖拽选区图片区域。参考 QQ 在 Mac 上的做法,当一个屏幕有选区了,另一个屏幕上禁止操作
多窗口互通的话,使用了 ipc 通讯。窗口选区后发给 main 进程,main 进程广播给其他窗口,其他窗口接收后禁止操作。
// 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 下让窗口显示在全屏窗口之上的话,需要一段神奇的代码,当然代码的写法是查搜出来的,但是具体原来还不是很清楚,貌似是一些 hack 的手段吧。
在我这我只能称之为"黑魔法"
下面一段代码放在创建截屏窗口的代码后面
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) 复制代码
经过上面的优化后,这个截图工具已经可以达到产品级了。当然还有一些不足的地方,比如跨屏截图,涂鸦,各种各样的体验细节吧,后面有时间优化完,再来和大家分享!!!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。