文章目录
- 一、目标原型
- 1. 目标
- 2. 原型设计
- 3. 原型初步实现
- 二、无边框窗口
- 1. 要点
- 2. 改造
- 三、可拖拽区
- 1. 要点
- 2. 改造
- 四、最小化、最大化、关闭
- 1. 要点
- 2. 改造
- 五、动态改变窗口大小
- 1. 要点
- 2. 改造
- 六、扩展
- 扩展1:在内嵌的独立项目中怎么实现拖拽、最大化、最小化、关闭?
- 扩展2:在``标签内嵌的页面中怎么实现拖拽、最大化、最小化、关闭?
- 扩展3:如何在Electron关闭时执行cmd命令
- 扩展4:系统托盘tray
- 七、源码下载
一、目标原型
1. 目标
- 实现一个无边框窗口,包括最小化、最大化、关闭、拖动等功能
- 动态改变窗口大小,即在页面跳转的时候根据需要改变窗口大小
2. 原型设计
(1) 登录窗口(500×300)
- 点击登录跳转到首页
(2) 首页(全屏)
- 跳转到首页,自动全屏
- 包括最小化、最大化、关闭,以及灰色区域可拖动整个窗口功能
3. 原型初步实现
创建login.html和index.html页面,实现原型页面基本效果,下文逐步改造,实现Electron无边框窗口。
二、无边框窗口
1. 要点
要创建无边框窗口,只需在BrowserWindow的options中将frame设置为 false:
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600, frame: false })
win.show()
通过将 transparent 选项设置为 true, 还可以使无框窗口透明:
let win = new BrowserWindow({ transparent: true, frame: false })
参考地址:https://electronjs.org/docs/api/frameless-window#%E9%80%8F%E6%98%8E%E7%AA%97%E5%8F%A3
2. 改造
在main.js
文件中添加frame:false
:
mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame:false,
webPreferences: {
/*preload: path.join(__dirname, 'preload.js')*/
nodeIntegration: true
}
})
注意: webPreferences的值,如果写preload无nodeIntegration,会影响后面最大最小化功能不起作用,暂不明白原因。
三、可拖拽区
1. 要点
- 应用程序需要在 CSS 中指定
-webkit-app-region: drag
来告诉 Electron 哪些区域是可拖拽的 - 在可拖拽区域内部使用
-webkit-app-region: no-drag
则可以将其中部分区域排除 - 拖动行为可能与选择文本冲突。 例如, 当您拖动标题栏时, 您可能会意外地选择标题栏上的文本。 为防止此操作, 您需要在可区域中禁用文本选择
.titlebar {
-webkit-app-region: drag;
-webkit-user-select: none;
}
- 在某些平台上,可拖拽区域不被视为窗口的实际内容,而是作为窗口边框处理,因此在右键单击时会弹出系统菜单。 要使上下文菜单在所有平台上都正确运行, 您永远也不要在可拖拽区域上使用自定义上下文菜单。
- 备注:如果你在某些页面设置了可拖拽区,跳转到一个新的页面,而这个新的页面没有设置可拖拽区,则会沿用上一页面的可拖拽区,感觉很奇怪,也没有相应的dom支持,就像取用的上一页面固定的像素区域一样(测试发现是这样,不准确之处请指正,谢谢)。如果拖拽区域不同,这种情况下,在新的页面中设置上拖拽区域即可。
2. 改造
修改index.html
页面中的样式,添加相应drag和no-drag特性
<div class="topbar" style="-webkit-app-region: drag;-webkit-user-select: none;">
<div class="logo fl">
<img src="images/logo.png" alt="">
<span>这里写软件名称</span>
</div>
<ul class="menu fl" style="-webkit-app-region: no-drag;">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
</ul>
<div class="min_max_close fr" style="-webkit-app-region: no-drag;">
<img src="images/min.png" id="min" alt="">
<img src="images/max.png" id="max" alt="">
<img src="images/close.png" id="close" alt="">
</div>
</div>
四、最小化、最大化、关闭
1. 要点
- render 进程通过 ipcRenderer 与 ipcMain 进行通讯,以通知 main 进程操作窗体。
2. 改造
(1) 在renderer.js中添加click事件,发送操作命令给主进程
let ipcRenderer = require('electron').ipcRenderer;
var max = document.getElementById('max');
if (max) {
max.addEventListener('click', () => {
//发送最大化命令
ipcRenderer.send('window-max');
//最大化图形切换
if (max.getAttribute('src') == 'images/max.png') {
max.setAttribute('src', 'images/maxed.png');
} else {
max.setAttribute('src', 'images/max.png');
}
})
}
var min = document.getElementById('min');
if (min) {
min.addEventListener('click', () => {
//发送最小化命令
ipcRenderer.send('window-min');
})
}
var close = document.getElementById('close');
if (close) {
close.addEventListener('click', () => {
//发送关闭命令
ipcRenderer.send('window-close');
})
}
(2) 在main.js中接收操作命令,做出相应处理
let ipcMain = require('electron').ipcMain;
//接收最小化命令
ipcMain.on('window-min', function() {
mainWindow.minimize();
})
//接收最大化命令
ipcMain.on('window-max', function() {
if (mainWindow.isMaximized()) {
mainWindow.restore();
} else {
mainWindow.maximize();
}
})
//接收关闭命令
ipcMain.on('window-close', function() {
mainWindow.close();
})
补充: 这里最大化和取消最大化时,修改图标的方法不合适,因为通过双击drag-area或者拉动窗口到屏幕边缘等操作,也可能修改窗口的状态。
因此,应该在主进程中监听窗口的最大化操作,然后发送命令给渲染进程:
mainWindow.on('maximize', function () {
mainWindow.webContents.send('main-window-max');
})
mainWindow.on('unmaximize', function () {
mainWindow.webContents.send('main-window-unmax');
})
在渲染进程中接收到相应命令,再进行处理:
ipcRenderer.on('main-window-max', (event) => {
max.classList.remove('icon-max');
max.classList.add('icon-maxed');
});
ipcRenderer.on('main-window-unmax', (event) => {
max.classList.remove('icon-maxed');
max.classList.add('icon-max');
});
五、动态改变窗口大小
1. 要点
思路同最小化、最大化、关闭,仅添加了页面调转。
2. 改造
(1) 在main.js中设置启动初始页面为login.html
mainWindow.loadFile('login.html')
(2) 在renderer.js中添加login按钮的click事件,发送最大化给主进程
var loginbtn = document.getElementById('login');
if (loginbtn) {
loginbtn.addEventListener('click', () => {
ipcRenderer.send('window-max');
location.href = "index.html";
})
}
六、扩展
扩展1:在内嵌的独立项目中怎么实现拖拽、最大化、最小化、关闭?
如果你只是用Electron打个包,用它的浏览器功能,里面还是一个完整的其它项目,比如一个普通的java web项目,相当于只是穿层衣服。怎么在里边实现以上最小化、最大化、关闭、拖动等功能呢?
- 首先创建一个完全独立的nodejs项目,创建一个index页面,其中有最小化、最大化、关闭按钮和拖拽区
- 在Electron项目index.html中直接跳转到独立项目页面
<script type="text/javascript">
window.location='http://localhost:8888/index.html';
</script>
- 实现可拖拽区
同上加上样式即可:style="-webkit-app-region: drag;-webkit-user-select: none;"
- 实现最小化、最大化、关闭
关键的有两步:
(1)在main.js
中new BrowserWindow加上nodeIntegration: true
mainWindow = new BrowserWindow({
width: 500,
height: 300,
frame: false,
webPreferences: {
nodeIntegration: true
}
})
(2)在独立项目中加上之前的renderer.js文件,在页面中引入renderer.js
<script type="text/javascript" src="renderer.js"></script>
其实不应该这样做,应该可以直接写require('./renderer.js')
的,但是这样要求在electron项目中把renderer.js export出来,由于我语法不熟,就直接拷过去了,先不研究了。
扩展2:在<webview>
标签内嵌的页面中怎么实现拖拽、最大化、最小化、关闭?
在webview标签上加上nodeintegration
属性:
<webview src="http://www.google.com/" nodeintegration></webview>
当有此属性时, webview 中的访客页(guest page)将具有Node集成, 并且可以使用像 require 和 process 这样的node APIs 去访问低层系统资源。
扩展3:如何在Electron关闭时执行cmd命令
//接收关闭命令
ipcMain.on('window-close', function() {
let closeProcess = child_process.exec('taskkill /f /im XXXXX.exe');
closeProcess.on('close', function (code) {
mainWindow.close();
})
})
扩展4:系统托盘tray
在任务栏右下角添加图标和右键菜单。查看Electron tray文档即可。