Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

利用 Electron 开发快速截图工具(二) #65

Open
YIXUNFE opened this issue Mar 27, 2016 · 4 comments
Open

利用 Electron 开发快速截图工具(二) #65

YIXUNFE opened this issue Mar 27, 2016 · 4 comments

Comments

@YIXUNFE
Copy link
Owner

YIXUNFE commented Mar 27, 2016

利用 Electron 开发快速截图工具(二)

上一篇我们讲到主界面开发完成,这一篇我们讲子界面的开发。

子界面的生命周期比较短,罗列如下:

  • 窗口打开并显示截图;
  • 用户截图点击确定后,发送截图数据至主进程;
  • 主进程关闭子界面。

我们来看看子界面中的一个个功能是如何实现的。


子界面中元素

canvas 元素

我选用了 canvas 作为截图的容器,主要是考虑到后续用户划取选区时比较方便。

  • DIV 难以实现遮罩中的选区透明的效果;
  • DIV 在从右至左,从下至上划取选区时需要不断的计算起始点坐标(左上角为起始点)。

按钮元素

子界面中有三个按钮:

  • 确定按钮:保存截图至剪贴板,并关闭子界面;
  • 保存按钮:保存截图为文件,并关闭子界面;
  • 关闭按钮:关闭子界面。

qq 20160327175631


子界面的打开与关闭

之前我们提到直接在主界面调用 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


@littledu
Copy link

请教,当我们改了主进程的文件后,有没办法让它不用重启就可以直接刷新生效的?官方的 debug 看得迷迷糊糊

@YIXUNFE
Copy link
Owner Author

YIXUNFE commented Apr 18, 2016

@littledu 开启 devtool 的时候,焦点在 devtool 上时可以直接 F5 刷新。

自动刷新没有尝试,但是拍脑袋想想可能可以通过 IDE 来配置,比如 WebStrom 可以配置自动刷新 NodeJS 程序。

@littledu
Copy link

不是自动刷新哈,是可以不用重新执行 electron --debug .来重新启动应用,要在 devtool 那里刷新就能刷新主进程?还没试过,晚上试试

@YIXUNFE
Copy link
Owner Author

YIXUNFE commented Apr 18, 2016

@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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants