Electron新玩法:讓你的桌面隨著音樂動起來!
創作靈感
開局先上效果與原始碼(兩個倉庫都是本人提交的,內容相同):
music-wallpaper: 基於Electron開發的桌面外掛,在桌布層顯示音訊視覺化效果 (gitee.com)
DLand-Team/music-wallpaper: 基於Electron開發的桌面外掛,在桌布層顯示音訊視覺化效果 (github.com備份倉庫)
最近閒來無事,想整點活,做點東西,給自己的開源攢點人氣,於是決定研究下Electron。在有一次逛github的時候,發現了一個好玩的外掛:
簡單說下,就是將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++與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)