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)