electron默认使用系统自带的导航栏,对于有UI定制需求的产品,需要自定义开发;实现的功能包括title展示、最大化、最小化、调整大小、拖拽位置等等;
主进程部分隐藏默认导航栏和菜单栏,监听子进程事件,操作客户端界面
1.通过frame:false和Menu.setApplicationMenu(null)隐藏默认导航栏和菜单;
const win = new BrowserWindow({
width: 1000,
height: 700,
minWidth: 1000,
minHeight: 700,
maxWidth:2560,
maxHeight:1440,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webSecurity: false,
webviewTag:true
},
transparent: true,
backgroundColor:'rgba(0,0,0,0)',
frame:false
});
Menu.setApplicationMenu(null);
2.封装titleBar.js实现最小化、最大化、还原、拖拽位置;
windows上设置=>显示设置=>缩放 不是100%时,窗口会拖拽放大,所以在拖拽时还需要设置大小;
拖拽调整位置时如果失去焦点则终止调整
const { ipcMain, screen } = require('electron')
exports.initTitleBar = function (win) {
let canMoving = false;
let movingInterval = null;
function windowMove(orderCanMoving) {
canMoving = orderCanMoving;
if (canMoving) {
// 读取原位置
const winPosition = win.getPosition();
// 获取当前缩放[width, height]
const winSize = win.getSize();
let winStartSize = { w: winSize[0], h: winSize[1] };
let winStartPosition = { x: winPosition[0], y: winPosition[1] };
let mouseStartPosition = screen.getCursorScreenPoint();
console.log('winPosition:', winPosition,',mouseStartPosition:',mouseStartPosition)
// 清除定时器
if (movingInterval) {
clearInterval(movingInterval);
}
// 新开
movingInterval = setInterval(() => {
// 实时更新位置
const cursorPosition = screen.getCursorScreenPoint();
const x = winStartPosition.x + cursorPosition.x - mouseStartPosition.x;
const y = winStartPosition.y + cursorPosition.y - mouseStartPosition.y;
// 更新位置的同时设置窗口原大小, windows上设置=>显示设置=>缩放 不是100%时,窗口会拖拽放大
win.setBounds({
x: x,
y: y,
width: winStartSize.w,
height: winStartSize.h,
})
}, 10);
} else {
if (movingInterval) {
clearInterval(movingInterval);
movingInterval = null;
}
}
}
ipcMain.on('min-app', () => {
if(win){
win.minimize()
}
})
ipcMain.on('max-app', () => {
if(win){
win.maximize()
}
})
ipcMain.on('restore-app', () => {
if(win){
if(win.isMaximized()){
// win.restore()
win.unmaximize();
}else{
win.maximize()
}
}
})
ipcMain.on('getIsMaximized',async(e,data)=>{
e.returnValue = {
isMaximized: win.isMaximized()
};
});
ipcMain.on('close-app', () => {
if(win) {
win.close()
}
})
ipcMain.on("window-move-open", (events, orderCanMoving) => {
windowMove(orderCanMoving);
});
ipcMain.on('setSize', (events,data) => {
if(win) {
win.setSize(data.width,data.height);
win.setMinimumSize(data.width,data.height);
win.center();
}
})
ipcMain.on('setResizable', (events,data) => {
if(win) {
win.setResizable(data);
}
})
// 监听窗口失去焦点事件
win.on('blur', () => {
windowMove(false);
});
}
子进程实现界面显示和功能交互
客户端顶部进行拖拽和调整大小有个边界值,需要添加判断逻辑
function Head({handleCheckAppUpdata,version}){
const { isInLive } = useIsInLive((state) => state);
const menuOptions = [
{
label:'刷新',value:'1'
},
// {
// label:'清除缓存',value:'2'
// },
{
label:'检查升级',value:'3'
},
// {
// label:'关于',value:'4'
// },
{
label:'退出',value:'5'
},
]
function menuAction(menu){
switch (menu.value) {
case '1':
EE.emit(EEName.APP_REFRESH);
break;
case '2':
message.success('清除成功');
break;
case '3':
handleCheckAppUpdata(true);
break;
case '5':
if(isInLive){
message.warning('正在直播中,请先结束直播');
}else{
logout();
}
break;
default:
break;
}
}
function handleMouseup(){
ipcRenderer.send('window-move-open',false)
}
useEffect(() => {
document.removeEventListener('mouseup', handleMouseup);
document.addEventListener('mouseup', handleMouseup);
}, []);
return (
<div className='headWrap'>
<div className='appTitle'
onMouseDown={(e)=>{
try {
//边界值5,小于5触发调整客户端大小事件
if(e.clientY<5){
ipcRenderer.send('window-move-open',false)
return
};
} catch (error) {
}
ipcRenderer.send('window-move-open',true)
}}
onMouseUp={()=>{ipcRenderer.send('window-move-open',false)}}
// onMouseLeave={()=>{ipcRenderer.send('window-move-open',false)}}
onDoubleClick={()=>{ipcRenderer.send('restore-app')}}
>直播助手<span className='version'>{version}</span></div>
<div className='appOptions'>
<PopoverPicker
className='appPopoverPicker'
options={menuOptions}
onChange={menuAction}
trigger='click'
showCheckIcon={false}
placement='bottom'
>
<div className='appMenu'></div>
</PopoverPicker>
{/* <div className='appRefesh'></div> */}
<div className='appLine'></div>
<div className='appMin' onClick={()=>{ipcRenderer.send('min-app')}}></div>
<div className='appRestore' onClick={()=>{ipcRenderer.send('restore-app')}}></div>
{/* <div className='appMax' onClick={()=>{ipcRenderer.send('max-app')}}></div> */}
<div className='appClose' onClick={()=>{
confirm({
title: '确认退出',
//@ts-ignore
icon: <ExclamationCircleFilled />,
content: `${!!isInLive?'正在直播中,请先结束直播':'确认退出直播助手吗?'}`,
onOk() {
!isInLive && ipcRenderer.send('close-app')
},
onCancel() {
},
okText:'确认',
cancelText:'取消'
});
}}></div>
</div>
</div>
)
}