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)