使用Electron开发一个吸色工具的心路历程

3,170 阅读4分钟

为什么要(zuo)

”世界上只有一种英雄主义,那就是认清自己的发际线之后依然热爱撸码“ -- 欧大强·罗兰

Electron是一个非常有趣的开源项目,“它可以让你使用纯 JavaScript 调用丰富的原生(操作系统) APIs 来创造桌面应用”。光看这官方简介就让人不住跃跃欲试,再加上市面上没找到能让我中意的桌面吸色工具,所以我决定使用Electron来开发一款。
下载地址:

需求与界面

我的软件,界面一定要好看,LOGO一定要大气,主色调要那种低沉性感又不失庄重,气势磅礴的黑。

desgin

(献丑了。果然设计师不是随便就能做的)

功能大致包括以下几点

  • 支持快捷键操作并可自定义。
  • 有历史选色记录。
  • 能自由切换色值。
  • 在已知透明度和背景色的条件下计算出当前取色器的rgba色值。
  • 支持最小化到托盘

基本思路

Electron没有API可以直接拿到当前指针所在位置的色值,但是提供了获取桌面资源的方法。在执行取色操作时使用desktopCapturer将当前桌面截图,然后在canvas上展示并使用getImageData() 获取指定色值。Easy。

prossess

遇到的问题

关于Electron的介绍网络有很多,上手难度也不算高,线程之间的关系与操作还有API文档写的非常详细,但这不意味着你(zuo)起来就能一帆风顺了。

Global变量无法获取

通过ipc和global我们很容易就能实现一个简易的状态管理机制,在这里我碰到了第一个坑。
因为remote复制对象而不是提供引用,所以在渲染进程中操作的global对象需要提前在主进程中定义初始值。
main process:

global.sharedObj = {prop1: null};

renderer process:

remote.getGlobal('sharedObj').prop1 = 125;

使用desktopCapturer截图有色差

通过desktopCapturer.getSources()API我们可以得到一个DesktopCapturerSource对象,再配合getUserMedia我们可以很方便的获取当前可用资源。

// In the renderer process.
const { desktopCapturer } = require('electron')

desktopCapturer.getSources({ types: ['window', 'screen'] }, (error, sources) => {
  ...
  navigator.mediaDevices.getUserMedia({
    ...
    video: {
      mandatory: {
        chromeMediaSource: 'desktop',
        chromeMediaSourceId: sources[0].id,
        ...
      }
    }
  }).then((stream) => {
    const video = document.querySelector('video')
    video.srcObject = stream
  }).catch((e) => ...)
})

以上是官方给的使用文档,将video的第一帧绘制到canvas上便成功对当前桌面进行截图。但事情并没有这么简单,在我阅片无数的双眼下,任何细微的差别都无处遁行,使用getUserMedia得到的图像竟然有色差

diff

我测算了一下,截图得到的图片和原图相比,绿色通道的值要高一些,所以我猜测造成色差的原因可能是由于浏览器使用的是sRGB色域,而显示器使用的是aRGB色域(希望有大佬能帮忙指正)。
虽然通过DesktopCapturerSource对象上的thumnail(NativeImage)能直接得到当前桌面的缩略图(截图)并且没有色差,但是desktopCapturer在工作中是会阻塞进程的,不推荐直接通过thumnail来获取百分百比例的桌面截图。所以最后我选择使用desktop-screenshot这个第三方node库来实现截图步骤。

写入文件路径报错

我使用了lowdb这个node库来储存设置的快捷键和历史色值,开发的时候一切正常,可打包后在Mac上运行起来就会报错EROFS: read-only file system

error

未签名的app似乎在mac上很不招待见,好在Electron提供了app.getPath(name)来获取文件路径的,我们无法确定用户会将软件放到哪个目录,所以我建议将需要被修改的文件放入系统文件夹或临时文件夹。

  app.getPath(name) // Electron
  or
  os.tmpdir() // node原生模块获得临时文件路径

打包优化

除了尽可能减少dependencies的依赖之外,基本无解。别问,问就是100M起。

结尾

虽然这是一个简单的项目,还有很多没来得及深入发掘。
虽然常伴小坑,但总的来说瑕不掩瑜,体验还是相当到位的。 如果你也想体验一把桌面应用开发,Electron是个非常靠谱的选择。
github

参考资料: