Electron新玩法:讓你的桌面隨著音樂動起來!

語言: CN / TW / HK

創作靈感

開局先上效果與原始碼(兩個倉庫都是本人提交的,內容相同):

music-wallpaper: 基於Electron開發的桌面外掛,在桌布層顯示音訊視覺化效果 (gitee.com)

DLand-Team/music-wallpaper: 基於Electron開發的桌面外掛,在桌布層顯示音訊視覺化效果 (github.com備份倉庫)

view.gif

最近閒來無事,想整點活,做點東西,給自己的開源攢點人氣,於是決定研究下Electron。在有一次逛github的時候,發現了一個好玩的外掛:

meslzy/electron-as-wallpaper: set your electron window as wallpaper behind desktop icons (github.com)

簡單說下,就是將Electron視窗插入到桌面圖示層和桌布層之間,以達到這個倉庫所說的“Electron As Wallpaper”的目的。於是,我也就打算用Electron做個桌面桌布功能的小玩具。

在去年我寫了個H5音樂播放器,研究了下音訊視覺化的效果,我決定通過Electron將這個效果在桌面上顯示出來。

H5音樂播放器原始碼: 基於Angular+ionic的音樂播放器,支援實時歌詞顯示,音訊視覺化 (gitee.com)

程式碼搭建

如圖,只有一個單一的音訊視覺化功能,直接用最基礎的Electron即可完成,最多整個ts進去。像是網上廣為流傳,說是做Electron桌面端就應該整合vite + ts + vue + pinia,我是覺得對於這種小玩具就沒必要哈,用不著高射炮打蚊子的做法。

建立專案方法就不贅述了,直接上package.json檔案:

json { "name": "music-wallpaper", "version": "1.0.0", "description": "A minimal Electron application", "main": "application-framework.js", "scripts": { "start": "npm run web-build:dev && electron ./application-framework.js", "package-build:win": "npm run web-build:prod && electron-packager . application --platform=win32 --arch=x64 --icon=favicon.ico --out=./out --asar --overwrite", "web-build:dev": "webpack --mode=development", "web-build:prod": "webpack --mode=production", "gyp-configure": "node-gyp configure", "gyp-rebuild": "node-gyp rebuild" }, "dependencies": { "babylonjs": "^5.46.0", "babylonjs-materials": "^5.46.0", "bindings": "^1.5.0", "node-addon-api": "^6.0.0" }, "devDependencies": { "@types/node": "^18.13.0", "electron": "^23.0.0", "electron-packager": "^17.1.1", "ts-loader": "^9.4.2", "typescript": "^4.9.5", "webpack": "^5.75.0", "webpack-cli": "^5.0.1" } }

可以看到主要依賴只用了node編譯工具與ts,其他的就是這兩個所需要的一些邊邊角角的東西了。也用了babylonjs,因為我後面考慮整個3D視覺化效果進去(目前你可以把它無視掉)。scripts裡面的執行程式碼分別為:

| 標題 | 作用 | | --- | --- | | start | 執行程式 | | package-build:win | 打包為win平臺可執行檔案 | | web-build:dev | 使用webpack development模式編譯頁面程式碼 | | web-build:prod | 使用webpack production模式編譯頁面程式碼 | | gyp-configure | 配置node-gyp | | gyp-rebuild | 使用node-gyp編譯C++檔案 |

非常基礎的開發方法,我連webpack熱更新都沒做(作者太懶了)。不過這樣降低配置量,降低學習成本,夠用即可,也是一種好事。因為我不喜歡上來就搞一大堆配置啥的。

核心原理

既然說到了node-gyp與C++編譯,那麼這裡就講講為啥需要這個東西。

上面提到的Electron As Wallpaper外掛,就是通過C++呼叫Windows API,將視窗嵌入到桌面圖示層與桌布層之間。這個外掛提供了npm包,可以在上述github倉庫地址中詳細檢視引入方法。但是在此我沒有通過npm去引入這個專案,我選擇將核心C++程式碼放到本專案中,手動編譯然後呼叫。這樣也能夠更好地去理解與學習Electron是如何通過C++與系統互動的。

原作者C++程式碼片段: 原作者C++程式碼

原作者C++與Electron互動實現程式碼片段: 原作者C++與Electron互動實現程式碼片段

具體呼叫方法,在本專案程式碼中可以看到,通過node-bindings實現呼叫,很簡單,就兩三個方法幾行程式碼的量。也可以去外掛作者的倉庫深入研究。

抓取系統音訊

Electron有一個名為desktopCapturer的模組,通過它來捕捉系統音訊。Electron文件地址: desktopCapturer | Electron (electronjs.org)

直接在Electron的index.html頁面中,通過呼叫navigator.mediaDevices.getUserMedia,使用如下配置引數,即可在回撥中獲取到系統音訊。然後使用AudioContext即可實現對音訊流的實時視覺化效果。

ts navigator.mediaDevices.getUserMedia({ // 配置引數 audio: { // @ts-ignore mandatory: { chromeMediaSource: 'desktop' } }, video: { // @ts-ignore mandatory: { chromeMediaSource: 'desktop' } } }).then(stream => { // 在回撥中獲取到的系統視訊與音訊流,我們選擇音訊流即可 let context = new AudioContext(); this.source = context.createMediaStreamSource(stream); this.analyser = context.createAnalyser(); this.analyser.fftSize = 2048; this.source.connect(this.analyser); });

關於AudioContext如何實現音訊視覺化,這是前端一個很成熟的技術,網上有很多教程和案例,在此就不做贅述了。

主要程式碼解析

| 檔名 | 作用 | | --- | --- | | application-framework.js | Electron程式入口檔案 | | application-preload.js | Electron頁面預載入檔案 | | application-main.ts | Electron頁面ts入口,也就是webpack編譯ts的入口 |

application-framework.js中,初始化程式視窗,新增系統托盤圖示等相關功能。以下為生成視窗的相關引數設定:

js mainWindow = new BrowserWindow({ show: false, // 開始時不顯示 frame: false, // 設定為false時可以建立一個無邊框視窗 focusable: false, // 視窗不能作為焦點被選中 transparent: true, // 使視窗透明 webPreferences: { preload: path.join(__dirname, 'application-preload.js'), } }); mainWindow.loadFile('index.html').then(() => { try { // 呼叫C++程式碼,嵌入視窗到圖示層與桌布層之間 bindings.attach(mainWindow.getNativeWindowHandle(), { transparent: true }); mainWindow.maximize(); // 視窗最大化 mainWindow.show(); // 等一切操作完成後,顯示視窗 } catch (e) { console.log(e); } });

對於application-preload.js,它是Electron在頁面執行其他指令碼之前預先載入指定的指令碼,在本專案中的作用就是建立Electron於頁面溝通的橋樑,實現通過托盤欄選單對頁面的控制。

application-main.ts就是頁面入口檔案,webpack編譯ts的入口,本專案使用的是最原始的方法,沒有框架,僅有將ts編譯為bundle.js供頁面使用這個單一的功能。

| 檔名 | 作用 | | --- | --- | | src/core/engine.ts | 引擎 | | src/effect/ | 各個視覺化效果的具體實現 | | src/wt/ | js dom api的封裝 |

src/core/engine.ts的作用是:通過實現一個“引擎”用來管理渲染迴圈以及各個效果的顯示與關閉,這種方法在遊戲開發中很常用。

```js // 將渲染控制在60幀 private loop(e: number) { if (e - this.frameTime >= 1000 / 60) { this.frameTime = e; this.render(); } requestAnimationFrame((e1: number) => { if (this.allState) { this.loop(e1); } }); }

// 在每一幀中更新各個特效 private render(): void { this.analyser.getByteFrequencyData(this.voiceHigh); this.e1_2d?.update(this.voiceHigh); this.e2_2d?.update(this.voiceHigh); this.e3_2d?.update(this.voiceHigh); } ```

src/effect/* 由於時間和篇幅的限制,各個效果的具體實現過程就不做詳細介紹了,有興趣可以直接看原始碼。每個效果都是作者自己手寫的。

src/wt/* 封裝了一些js dom api,用來生成頁面上的dom元素。因為本專案也沒用任何前端框架,作者是java gui開發出身的,很喜歡這種直接懟程式碼的gui寫法。

結尾

初次寫文章,也不知道咋結尾,也不知道該講的講清楚沒,有問題歡迎留言討論。另外本專案提供了打包好的檔案,解壓即可執行。release釋出在gitee倉庫中。歡迎大家給點star,star就是我整活的動力

music-wallpaper: 基於Electron開發的桌面外掛,在桌布層顯示音訊視覺化效果 (gitee.com)