blog
blog copied to clipboard
利用 Electron 开发快速截图工具(二)
利用 Electron 开发快速截图工具(二)
上一篇我们讲到主界面开发完成,这一篇我们讲子界面的开发。
子界面的生命周期比较短,罗列如下:
- 窗口打开并显示截图;
- 用户截图点击确定后,发送截图数据至主进程;
- 主进程关闭子界面。
我们来看看子界面中的一个个功能是如何实现的。
子界面中元素
canvas 元素
我选用了 canvas
作为截图的容器,主要是考虑到后续用户划取选区时比较方便。
-
DIV
难以实现遮罩中的选区透明的效果; -
DIV
在从右至左,从下至上划取选区时需要不断的计算起始点坐标(左上角为起始点)。
按钮元素
子界面中有三个按钮:
- 确定按钮:保存截图至剪贴板,并关闭子界面;
- 保存按钮:保存截图为文件,并关闭子界面;
- 关闭按钮:关闭子界面。
子界面的打开与关闭
之前我们提到直接在主界面调用 window.close()
关闭窗口,但这会在子界面中引起报错:
Uncaught RangeError: Maximum call stack size exceeded
在 Electron 教程中提到了一点,在网页中调用 GUI API 很危险,应该让主进程执行 GUI 操作。
所以渲染进程(即网页)的关闭应该通过主进程执行。
Electron 提供了我们 ipc
模块实现主进程与渲染进程的通信,需要注意的是,不同的进程中需要引入不同的 ipc
模块。
//in main process
var ipc = require('electron').ipcMain;
//in renderer process
var ipc = require('electron').ipcRenderer;
主界面点击截图按钮时会通知主进程打开子界面:
//in renderer process
document.getElementById('capture').addEventListener('click', function () {
ipc.send('create-sub-window')
})
//in main process
ipc.on('create-sub-window', function (e, wh) {
subWindow = new BrowserWindow({...})
subWindow.loadURL('file://' + __dirname + '/sub.html')
})
当用户点击子界面的关闭按钮时,子界面通过 ipc
模块发送一个“关闭”消息至主进程,通知关闭子界面。
//in renderer process
ipc.send('close-subwindow')
//in main process
ipc.on('close-subwindow', function () {
subWindow.close()
})
用户截图
用户截图部分主要是通过用户在 canvas
上划取选区,最后得到一个参数对象:
{
x: x,
y: y,
width: width,
height: height
}
由于截图部分和 Electron 并不是很搭噶,大家可以从 项目 中查看相关代码。
这个参数可以用于 Electron 进行网页截图。
// electron browserWindow capture page
subWindow.capturePage({
x: x,
y: y,
width: width,
height: height
}, function (image) {
...
})
capturePage
方法接受两个参数,第一个参数是截图的数据参数,包含了截图的起始点与宽高。值得注意的是,高宽必须是正数(canvas 的 rect
方法接受负数的高宽值)。第二个参数就是截图后的回调方法,会参入截得的 nativeImage 对象。如果省略第一个参数,会获得整个网页的截图哦。
网页截图后,需要保存至剪贴板。这时候需要调用 Electron 的剪贴板模块:
var clipboard = require('electron').clipboard
clipboard.writeImage(image) //nativeImage object
向剪贴板中写入图片需要使用 writeImage
方法,并且需要传入从 subWindow.capturePage
方法中得到的 nativeImage
对象。完成后我们可以从 Ctrl + V
的快捷操作中验证。
保存文件
保存文件功能本身是通过 Node.js 的文件系统完成的,但是可以结合 Electron 的对话框,可以使用户自己决定保存目录。
Electron 的 dialog
模块只有 4 个方法可以使用,这里我们使用 showSaveDialog
方法让用户指定目录和文件名。
var dialog = electron.dialog,
fs = require('fs')
dialog.showSaveDialog({title: '请选择保存路径', defaultPath: 'E:/', filters: [
{ name: 'Images', extensions: ['png'] }
]}, function (p) {
fs.writeFile(p, image.toPng(), function () {
...
})
})
由于需要用到截图得到的 nativeImage
对象,需要在主进程中组织代码。Node.js 中的 writeFile
方法支持写入 buffer
数据,所以第二个参数我们使用了 image.toPng
方法,它会返回图片对应格式的 buffer
数据,如果想要 jpg 格式的话可以使用 image.toJpeg
方法。
image.toJpeg(quality)
打包应用
打包在开胃篇中提到过,使用 electron-packager
可以简单方便的实现打包。
npm install electron-packager --save-dev
为了使用方便,我们在 package.json
中加上一个打包命令:
"scripts": {
"start": "electron main.js",
"package": "electron-packager ./ screenCapturer --all --out ~/Desktop/screenCapturer --version 0.37.2 --overwrite --icon=./app/app-icon.ico"
}
以后每次打包,直接跑一下命令即可。
npm run-script package
第一次打包时需要下载相关的资源包,之后就不需要下载了。
项目地址
Thanks
请教,当我们改了主进程的文件后,有没办法让它不用重启就可以直接刷新生效的?官方的 debug 看得迷迷糊糊
@littledu 开启 devtool 的时候,焦点在 devtool 上时可以直接 F5 刷新。
自动刷新没有尝试,但是拍脑袋想想可能可以通过 IDE 来配置,比如 WebStrom 可以配置自动刷新 NodeJS 程序。
不是自动刷新哈,是可以不用重新执行 electron --debug .
来重新启动应用,要在 devtool 那里刷新就能刷新主进程?还没试过,晚上试试
@littledu
可以尝试这个包:https://github.com/Quramy/electron-connect
// Restart browser process
gulp.watch('app.js', electron.restart);
// Reload renderer process
gulp.watch(['index.js', 'index.html'], electron.reload);