窗口插件开发详解
窗口插件是最常用的一类。这一节把它拆开讲:入口是怎么被加载的、生命周期有哪几个、窗口能力怎么控制、以及怎么做到跟主题和语言一致。
入口是怎么被加载的
窗口插件在 manifest.json 里用 main 对象描述入口。三个字段撑起基本形态:
{
"main": {
"url": "index.html",
"width": 640,
"height": 480
}
}
url 指向入口页面,Eagle 会把它载入一个独立的浏览器窗口。加了 minWidth/minHeight/maxWidth/maxHeight 之后,用户就拖不出你预设的范围。
一个配置完整的主窗口示例:
{
"id": "LBCZE8V6LPCKD",
"version": "1.0.0",
"name": "窗口插件",
"logo": "/logo.png",
"keywords": [],
"devTools": false,
"main": {
"url": "index.html",
"width": 640,
"height": 480,
"minWidth": 400,
"minHeight": 300,
"maxWidth": 1200,
"maxHeight": 900,
"alwaysOnTop": false,
"frame": true,
"fullscreenable": true,
"maximizable": true,
"minimizable": true,
"resizable": true,
"backgroundColor": "#ffffff",
"childWindow": false,
"followCursor": false,
"multiple": false,
"runAfterInstall": false
}
}
frame、resizable、minimizable、maximizable、fullscreenable 这几个布尔值共同决定了用户能对窗口做什么。全部设成 false 再配上 frame: false,你就得到了一个完全受控的「面板」——这是很多设计类插件的选择,因为工具型界面本来就不需要被最大化和拖动。
生命周期:五个必须知道的回调
窗口插件有一组完整的事件回调,都挂在 eagle 上:
// 窗口建立时触发一次,适合做初始化
eagle.onPluginCreate((plugin) => {
console.log(plugin.manifest.name);
console.log(plugin.path);
});
// 用户在插件面板点击插件时触发
eagle.onPluginRun(() => {
console.log('用户触发了插件');
});
// 窗口显示时触发(从隐藏恢复也算)
eagle.onPluginShow(() => {
console.log('窗口显示');
});
// 窗口隐藏时触发
eagle.onPluginHide(() => {
console.log('窗口隐藏');
});
// 窗口关闭前触发
eagle.onPluginBeforeExit(() => {
console.log('插件将退出');
});
注意 onPluginCreate 和 onPluginRun 的区别:前者是窗口创建,后者是用户点击。multiple: false 时窗口是复用的,第二次点击插件可能只触发 onPluginRun 而不重建窗口。如果你的初始化逻辑写在 onPluginCreate 里靠第二次点击刷新,那是行不通的——写在 onPluginRun 里才对。
阻止窗口关闭也可以,但要换一种写法:
eagle.onPluginBeforeExit(() => {
console.log('插件将退出');
});
// 想拦截关闭时,注册 onbeforeunload 并返回
window.onbeforeunload = (event) => {
return event.returnValue = false;
};
用 window API 控制窗口
eagle.window 提供了一整套窗口控制方法,覆盖日常需求:
// 显示与焦点
await eagle.window.show(); // 显示并聚焦
await eagle.window.showInactive(); // 显示但不抢焦点
await eagle.window.hide();
await eagle.window.focus();
// 最小化与最大化
await eagle.window.minimize();
await eagle.window.restore();
await eagle.window.maximize();
await eagle.window.unmaximize();
let isMax = await eagle.window.isMaximized();
// 全屏
await eagle.window.setFullScreen(true);
await eagle.window.setFullScreen(false);
// 尺寸与位置
await eagle.window.setSize(720, 480);
let [w, h] = await eagle.window.getSize();
await eagle.window.setBounds({ x: 440, y: 225, width: 800, height: 600 });
await eagle.window.setPosition(100, 200);
let [x, y] = await eagle.window.getPosition();
// 其他
await eagle.window.setAlwaysOnTop(true); // 置顶
await eagle.window.setOpacity(0.8); // 透明度 0~1
await eagle.window.setResizable(false);
await eagle.window.setBackgroundColor("#FFFFFF");
await eagle.window.flashFrame(true); // 窗口边框闪烁,吸引注意
其中几个特别值得注意的设计意图:
setOpacity(0.5)配合setIgnoreMouseEvents(true)能做出「悬浮在最上层、鼠标穿透」的桌面挂件。这在色彩取样器、标尺类工具里很常见。setBackgroundColor主要是防闪烁的——窗口弹出的那一瞬间 HTML 还没加载完,用户看到的是纯白色块。设一个接近你最终界面底色的颜色,视觉上就顺滑很多。capturePage(rect)能截取窗口画面并返回 NativeImage,可以拿来做「导出当前预览图」这类功能。
跟随主题变化
Eagle 有六套主题配色:LIGHT、LIGHTGRAY、GRAY、BLUE、PURPLE、DARK。用户切换时插件必须跟着变,否则白底插件套在深色主界面旁边会很突兀。
eagle.onPluginCreate(async () => {
// 读取当前主题
const theme = await eagle.app.theme;
document.body.setAttribute('theme', theme);
});
// 监听主题变化
eagle.onThemeChanged((theme) => {
document.body.setAttribute('theme', theme);
});
推荐把主题挂在 body 的属性上,再用 CSS 选择器统一切换,而不是在 JS 里逐个元素改颜色:
body { background: #ffffff; color: #000000; }
body[theme="DARK"], body[theme="GRAY"], body[theme="BLUE"], body[theme="PURPLE"] {
background: #2c2c2c; color: #ffffff;
}
body[theme="LIGHTGRAY"] {
background: #f0f0f0; color: #000000;
}
典型开发流程
- 在
manifest.json里定好main的尺寸约束、backgroundColor、frame等字段。 - 在
index.html里搭结构,引入js/plugin.js。 - 在
plugin.js的onPluginCreate里读一次eagle.app.theme设置初始主题。 - 在
onPluginRun里做每次点击都要执行的动作(刷新数据、重置状态)。 - 注册
onThemeChanged应对主题切换。 - 按
F12逐个尺寸档位测试布局,尤其是minWidth那个极端值。 - 打包前把
devTools改成false。
常见坑
multiple: false时把刷新逻辑写死在onPluginCreate。 窗口复用后第二次点击不会重新触发创建回调,用户会觉得「插件没反应」。刷新类逻辑应该放onPluginRun。忘记处理窗口最小尺寸。 你的界面在 640 宽下好看,用户把窗口拖到 300 宽就全乱了。用
minWidth/minHeight兜住,并在测试时手动缩到最小验证一遍。延伸注意: * 在窗口里
setInterval之后不清理。 窗口复用时定时器不会被销毁,会越积越多导致内存上涨。清理放在onPluginHide或onPluginBeforeExit里。整理人:WorkBuddy
