electron自定义快捷窗口按钮,大小窗口切换

关注公众号: 微信搜索 前端工具人 ; 收货更多的干货

原文链接: 自己掘金文章: https://juejin.cn/post/7067815153374330888/

一、需求

主要是一下几个常见的需求:

  • 自定义顶部菜单栏, 可拖拽;
  • 自定义最小化、最大化、退出按钮、刷新按钮(类似浏览器的重新加载、用于开发阶段调试);
  • 小窗口 - 中窗口 - 全屏窗口, 相互切换;

二、electron 解读

electron 区分了两种进程:主进程和渲染进程

2.1 主进程:

  • 创建进程、窗口...
  • 控制应用生命周期(启动、退出APP、事件监听..)
  • 调用系统底层功能 (Electron API)、调用原生APINode.js 与本地交互...)

2.2 渲染进程

  • 主要是内置 Chromium 浏览, 来实现页面的渲染;
  • 可以理解成 electron 渲染进程 为 Chromium 的窗口; 所以和日常开发区别不大

三、需求实现

项目入口文件 render.js, 主进程事件文件 ipc.event.js

render.js

'use strict'

import { app, protocol, BrowserWindow, Menu } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
import initIpcEvent from './services/ipc.event'
const path = require('path');

const isDevelopment = process.env.NODE_ENV !== 'production'

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

async function createWindow () {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 400,
    height: 500,
    center: true,
    frame: false,
    useContentSize: true,
    // resizable: false,
    webPreferences: {
      webSecurity: false,
      enableRemoteModule: true,
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
    },
    icon: path.join(__dirname, '../public/favicon32.ico')
  })

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    // win.webContents.openDevTools()
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }
  win.setMenu(null)
  global.mainWindow = win
  // 初始化进程之间事件监听
  initIpcEvent()
  // 隐藏菜单
  createMenu()
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      // await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

// 设置菜单栏
function createMenu() {
  // darwin表示macOS,针对macOS的设置
  if (process.platform === 'darwin') {
    const template = [{
      label: 'Electron',
      submenu: [{
        role: 'about'
      }, {
        role: 'quit'
      }]
    }]
    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)
  } else {
    // windows及linux系统
    Menu.setApplicationMenu(null)
  }
}

ipc.event.js

import { ipcMain, app, BrowserWindow } from 'electron'

export default function () {
  ipcMain.on('toggle-mini', (event, params) => {
    if (params.value) {
      global.mainWindow.hide()
    } else {
      global.mainWindow.show()
    }
  })

  ipcMain.on('window-min', () => {
    global.mainWindow.minimize()
    global.mainWindow.setResizable(true)
  })

  ipcMain.on('window-login', () => {
    global.mainWindow.setMinimumSize(400, 500)
    global.mainWindow.center()
    global.mainWindow.setResizable(false)
  })

  ipcMain.on('window-password', () => {
    global.mainWindow.setSize(1366, 768)
    global.mainWindow.center()
    global.mainWindow.setResizable(true)
  })

  ipcMain.on('window-max', () => {
    if (global.mainWindow.isMaximized()) {
      global.mainWindow.restore()
    } else {
      global.mainWindow.maximize()
    }
    // global.mainWindow.setMinimumSize(1600, 900)
    global.mainWindow.setMinimumSize(1200, 800)
    global.mainWindow.center()
  })

  ipcMain.on('window-hide', () => {
    global.mainWindow.hide()
  })

  ipcMain.on('window-show', () => {
    global.mainWindow.show()
  })

  ipcMain.on('window-refresh', () => {
    global.mainWindow.reload();
  })

  // 关闭当前窗口
  ipcMain.on('window-close', () => {
    console.log("window-close")
    global.mainWindow.close()
  })

  // 关闭所有窗口
  ipcMain.on('window-all-close', () => {
    console.log("window-all-close")
    const wins = BrowserWindow.getAllWindows()
    for (let i = 0; i < wins.length; i++) {
      wins[i].close()
    }
  })

  // 所有窗口都将立即被关闭,而不询问用户,而且 before-quit 和 will-quit 事件也不会被触发。
  ipcMain.on('app-exit', () => {
    app.exit()
  })

  ipcMain.on('quit-and-open', (event, data) => {
    global.downloadFile = data
    app.quit()
  })
}

3.1 自定义顶部导航栏

首先要隐藏调自带的顶部导航栏

// createMenu() 方法就是隐藏顶部导航栏
/ 拖拽,样式  -webkit-app-region: drag;

3.2 自定义最小化、最大化、退出按钮、刷新按钮;

<!-- 顶部导航栏 -->
<template>
  ...
  <header class="common-header">
    <i class="el-icon-refresh-right" title="刷新" @click="onChangeWindow('refresh')"></i>
    <i class="el-icon-switch-button" title="退出登录" @click="onChangeWindow('logout')"></i>
    <i class="el-icon-minus" title="最小化" @click="onChangeWindow('min')"></i>
    <i v-show="isMax" class="el-icon-copy-document" title="还原" @click="onChangeWindow('scale')"></i>
    <i v-show="!isMax" class="max-window" title="最大化" @click="onChangeWindow('scale')">
      <span></span>
    </i>
    <i class="el-icon-close" title="关闭" @click="onChangeWindow('close')"></i>
  </header>
  ...
</template>
<script>
import { ipcRenderer, remote } from 'electron'
...
// 窗口切换
onChangeWindow (type) {
  switch (type) {
    case 'min':
      ipcRenderer.send('window-min')
      break;
    case 'scale':
      ipcRenderer.send('window-max')
      const winInfo = remote.getCurrentWindow()
      this.isMax = winInfo.isMaximized()
      break;
    case 'close':
      ipcRenderer.send('window-min')
      break;
    case 'logout':
      ipcRenderer.send('window-min')
      setTimeout(() => {
        this.$router.push('/')
        ipcRenderer.send('window-login')
        ipcRenderer.send('window-show')
      }, 150)
      break;
    case 'refresh':
      ipcRenderer.send('window-refresh')
      break;
  }
...
</script>

3.3 小窗口 - 中窗口 - 全屏窗口, 相互切换;

  • 初始化登录界面是小窗口类似于微信登录一样 400 * 500
  • 登录后跳转到程序主界面, 全屏
  • 忘记密码界面(中窗口)1366 * 788

自己开发时,在全屏状态下切换回小窗口切换不了,百度了蛮久,也没结果;

后面发现改变全屏状态后就能切换...

方法:先最小化窗口、在 setMinimumSize 改变尺寸, 在显示, 相当于加了个过渡一下,变换过程也没那么死板

具体看3.2onChangeWindow 里的 logout 方法

有不对之处欢迎指正, 代码有删减,没做测试;

只是作为分享,让有需要的少走弯路,节省百度时间

posted @ 2022-02-23 16:15  会写代码的赖先生  阅读(1853)  评论(0编辑  收藏  举报