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)