最近有个需求,做一套基于 Electron 的项目打包工具,并适配老 Web 项目的分辨率及比例,要求窗口缩放按照比例等比放大缩小。
正文
各个项目的适配分辨率不一,而为了伪装成桌面程序,又需要让它们尽量贴满整个窗口并等比缩放,所以这个问题可以拆成两个小问题:
获得正确分辨率并将 BrowserWindow 初始大小更新为正确分辨率
拦住窗口的缩放操作,计算等比缩放的窗口大小并应用。
1.获取并设置初始大小
Electron 新建窗口就是新建 BrowserWindow,由于每个 Web 项目的分辨率不同,窗口创建时指定的大小没用,需要在获取到项目分辨率后重新设置窗口大小。
为了不修改老项目的网页代码,我使用 BrowserWindow 构造方法中的 webPreferences.preload 项将 js 文件注入到原来的 html 页面中。
const mainWindow = new BrowserWindow({
width: 960,
height: 630,
// icon:app.getAppPath() + "/favicon.ico",
icon:path.join(__dirname,"src/TemplateData", 'favicon.ico'),
webPreferences: {
preload: path.join(__dirname, '/preload.js'),//此项会在运行时自动注入网页中
devTools:false,
nodeIntegration:true
},
show:false,
});
mainWindow.loadFile('src/index.html');
mainWindow.once("ready-to-show",()=>{
mainWindow.show();
})
实际上 preload 加载的 js 文件可以直接调用 node 而无需设置 nodeIntegration。在 preload.js 中获取老项目配置的宽高后,直接通过ipcRenderer发送至后端。
//preload.js
const { ipcRenderer} = require('electron');
window.addEventListener("DOMContentLoaded",OnLoadFinish);
function OnLoadFinish(){
let realSize = GetRealSize();
ipcRenderer.send("realSize",realSize);
}
function GetRealSize(){
//获取 DOM,之后便可以获取其宽高
//注意,如果宽高是 100px 这种字符串,需要手动将其搞成 Number。
let mainContent = document.getElementById("xxx");
let w = mainContent.style.width;
let h = mainContent.style.height;
if(typeof(w) == "string"){
w = parseInt(w.split("px")[0]);
h = parseInt(h.split("px")[0]);
}
return {
width:w,
height:h
}
}
然后,在 Electron 主线程中监听realSize事件,事件触发后重新设置窗口大小。
//main.js
let realSize = null;
ipcMain.once("realSize",(e,args)=>{
realSize = {width:args.width,height:args.height};
mainWindow.setMinimumSize(parseInt((args.width) / 2),parseInt((args.height + 30)/2));
mainWindow.setContentSize(args.width,args.height);//注意此项设置的是ContentSize,此项大小不包括标题栏。
})
2. 缩放时等比缩放
等比缩放很简单,上面已经获取了 realSize,每次将要缩放时,我们拦截住这个请求,并手动设置窗口大小为想要的大小。
Electron 中的 BrowserWindow 在每次重新缩放大小前会触发will-resize事件。
mainWindow.on("will-resize", resizeWindow);
function resizeWindow(event, newBounds){
if(realSize == null){
return;
}
const win = event.sender;
event.preventDefault();//拦截,使窗口先不变
const currentSize = win.getSize();
const widthChanged = (currentSize[0] != newBounds.width);//判断是宽变了还是高变了,两者都变优先按宽适配
if(widthChanged){
win.setContentSize(newBounds.width, parseInt(newBounds.width / (realSize.width / realSize.height) + 0.5));
} else {
win.setContentSize(parseInt((realSize.width / realSize.height) * newBounds.height + 0.5), newBounds.height);
}
}
好了,到这里设置初始化分辨率和等比缩放已经实现了。
3. 其他问题
缩放窗口之后,有时可能需要重新设置 html 中的某些 DOM 样式,可以将相关代码一并写入 preload.js 中。
渲染进程中监听 window 的 resize 事件可以获取窗口大小变化的回调。
//preload.js
window.addEventListener("resize",onResize);
function onResize(){
//做一些其他事情,例如重新设置div大小,让他等比例铺满窗口
}