使用Electron開發一個吸色工具的心路歷程

ChuckOu發表於2019-04-07

為什麼要(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

參考資料:

相關文章