electron-vite【实战】自定义标题栏【组件封装】(含异形标题栏,指定区域拖拽,窗口置顶,窗口最小化,窗口最大化,取消最大化,隐藏窗口到托盘等)

发布于:2024-12-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

效果预览

在这里插入图片描述

技术要点

透明背景

src/main/index.ts 的 new BrowserWindow 中添加

    transparent: true, // 设置窗口背景透明
    frame: false, // 隐藏窗口边框

仅图标和标题部分可拖拽

在这里插入图片描述
仅图标和标题部分添加样式 drag

.drag {
  -webkit-app-region: drag;
}

图标与标题栏的融合

  • 标题栏的背景色采用图标的背景色
bg-[#0baaf5]
  • 标题栏添加顶部的外边距,值为图标的半径
mt-30px

图标的样式如下:

<img :src="icon" alt="logo" class="fixed h-60px top-[0px] drag" />

窗口置顶

在这里插入图片描述

      <Icon
        v-if="ifTop"
        icon="stash:pin-thumbtack-solid"
        :class="iconClassString"
        @click="cancle_top"
      />

      <Icon v-else icon="stash:pin-thumbtack" :class="iconClassString" @click="top" />
const ifTop = ref(false)

// 窗口置顶
function top() {
  window.electron.ipcRenderer.send('top')
  ifTop.value = true
}

function cancle_top() {
  window.electron.ipcRenderer.send('cancle_top')
  ifTop.value = false
}

src/main/index.ts 中

  ipcMain.on('top', () => {
    mainWindow.setAlwaysOnTop(true)
  })

  ipcMain.on('cancle_top', () => {
    mainWindow.setAlwaysOnTop(false)
  })

窗口最小化

在这里插入图片描述

 <Icon icon="qlementine-icons:windows-minimize-16" :class="iconClassString" @click="min" />
function min() {
  window.electron.ipcRenderer.send('min')
}

src/main/index.ts 中

  ipcMain.on('min', () => {
    mainWindow.minimize()
  })

窗口最大化 / 取消最大化

在这里插入图片描述
在这里插入图片描述

      <Icon
        v-if="ifMax"
        icon="qlementine-icons:windows-unmaximize-16"
        :class="iconClassString"
        @click="cancel_max"
      />

      <Icon
        v-else
        icon="qlementine-icons:windows-maximize-16"
        :class="iconClassString"
        @click="max"
      />
const ifMax = ref(false)

function max() {
  window.electron.ipcRenderer.send('max')
}

function cancel_max() {
  window.electron.ipcRenderer.send('cancel_max')
}

window.electron.ipcRenderer.on('unmaximize', () => {
  ifMax.value = false
})

window.electron.ipcRenderer.on('maximize', () => {
  ifMax.value = true
})

src/main/index.ts 中

  ipcMain.on('max', () => {
    mainWindow.maximize()
  })

  ipcMain.on('cancel_max', () => {
    mainWindow.unmaximize()
  })

因将窗口拖拽到屏幕边缘时,会自动触发最大化,调整窗口大小会退出最大化,则需监听窗口最大化和退出最大化事件,由主进程通知渲染进程状态,以便切换图标。[ 不适应于本范例,因本范例隐藏了 frame ]

  // 窗口变为最大化状态
  mainWindow.on('maximize', () => {
    mainWindow.webContents.send('maximize')
  })

  // 窗口从最大化状态退出
  mainWindow.on('unmaximize', () => {
    mainWindow.webContents.send('unmaximize')
  })

隐藏窗口到托盘

在这里插入图片描述

<Icon icon="si:close-fill" :class="iconClassString" @click="hide" />
function hide() {
  window.electron.ipcRenderer.send('hide')
}

src/main/index.ts 中

  ipcMain.on('hide', () => {
    // 使窗口不显示在任务栏中
    mainWindow.setSkipTaskbar(true)
    mainWindow.hide()
  })

代码实现

src/renderer/src/components/TitleBar.vue

<script setup lang="ts">
import icon from '../../../../resources/icon.png'
const props = defineProps({
  title: {
    type: String
  }
})

function hide() {
  window.electron.ipcRenderer.send('hide')
}

const ifTop = ref(false)

// 窗口置顶
function top() {
  window.electron.ipcRenderer.send('top')
  ifTop.value = true
}

function cancle_top() {
  window.electron.ipcRenderer.send('cancle_top')
  ifTop.value = false
}

function min() {
  window.electron.ipcRenderer.send('min')
}

const ifMax = ref(false)

function max() {
  window.electron.ipcRenderer.send('max')
}

function cancel_max() {
  window.electron.ipcRenderer.send('cancel_max')
}

window.electron.ipcRenderer.on('unmaximize', () => {
  ifMax.value = false
})

window.electron.ipcRenderer.on('maximize', () => {
  ifMax.value = true
})

const iconClassString = 'cursor-pointer hover:bg-blue-500 block h-full px-2'
</script>

<template>
  <img :src="icon" alt="logo" class="fixed h-60px top-[0px] drag" />
  <div class="flex items-center bg-[#0baaf5] text-white h-30px mt-30px">
    <div class="drag flex-1 pl-70px font-bold text-white">{{ props.title }}</div>
    <div class="flex h-full">
      <Icon
        v-if="ifTop"
        icon="stash:pin-thumbtack-solid"
        :class="iconClassString"
        @click="cancle_top"
      />

      <Icon v-else icon="stash:pin-thumbtack" :class="iconClassString" @click="top" />

      <Icon icon="qlementine-icons:windows-minimize-16" :class="iconClassString" @click="min" />

      <Icon
        v-if="ifMax"
        icon="qlementine-icons:windows-unmaximize-16"
        :class="iconClassString"
        @click="cancel_max"
      />

      <Icon
        v-else
        icon="qlementine-icons:windows-maximize-16"
        :class="iconClassString"
        @click="max"
      />

      <Icon icon="si:close-fill" :class="iconClassString" @click="hide" />
    </div>
  </div>
</template>

页面使用

<TitleBar title="邀请函" />

src/main/index.ts

import { app, shell, BrowserWindow, ipcMain, Tray, Menu } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'

function createWindow(): void {
  const mainWindow = new BrowserWindow({
    // 自定义图标
    icon: icon,
    // 自定义窗口宽度
    width: 360,
    // 自定义窗口高度
    height: 430,
    //默认隐藏窗口
    show: false,
    // 隐藏窗口标题栏
    titleBarStyle: 'hidden',
    // 隐藏默认菜单
    autoHideMenuBar: true,
    // 不可改变窗口大小
    resizable: false,
    // 不可改变窗口大小
    maximizable: false,

    transparent: true, // 设置窗口背景透明
    frame: false, // 隐藏窗口边框

    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      sandbox: false
    }
  })

  // 托盘
  const tray = new Tray(icon)

  const contextMenu = [
    {
      label: '退出',
      click: function () {
        app.exit()
      }
    }
  ]

  const menu = Menu.buildFromTemplate(contextMenu)

  tray.setToolTip('EC编程俱乐部')

  tray.setContextMenu(menu)

  tray.on('click', () => {
    // 使窗口显示在任务栏中
    mainWindow.setSkipTaskbar(false)
    mainWindow.show()
  })

  // IPC通信
  ipcMain.on('showPage_home', () => {
    // 窗口可调整大小
    mainWindow.setResizable(true)
    mainWindow.setSize(800, 720)
    // 窗口居中
    mainWindow.center()
    // 窗口可最大化
    mainWindow.setMaximizable(true)
  })

  ipcMain.on('top', () => {
    mainWindow.setAlwaysOnTop(true)
  })

  ipcMain.on('cancle_top', () => {
    mainWindow.setAlwaysOnTop(false)
  })

  ipcMain.on('hide', () => {
    // 使窗口不显示在任务栏中
    mainWindow.setSkipTaskbar(true)
    mainWindow.hide()
  })

  ipcMain.on('min', () => {
    mainWindow.minimize()
  })

  ipcMain.on('max', () => {
    mainWindow.maximize()
  })

  ipcMain.on('cancel_max', () => {
    mainWindow.unmaximize()
  })

  mainWindow.on('ready-to-show', () => {
    // 自定义标题
    mainWindow.setTitle('EC编程俱乐部')
    mainWindow.show()
  })

  // 窗口变为最大化状态
  mainWindow.on('maximize', () => {
    mainWindow.webContents.send('maximize')
  })

  // 窗口从最大化状态退出
  mainWindow.on('unmaximize', () => {
    mainWindow.webContents.send('unmaximize')
  })

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url)
    return { action: 'deny' }
  })

  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
  } else {
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
  }
}

app.whenReady().then(() => {
  // Set app user model id for windows
  electronApp.setAppUserModelId('com.electron')

  app.on('browser-window-created', (_, window) => {
    optimizer.watchWindowShortcuts(window)
  })

  createWindow()

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

ipcMain.on('quit', () => {
  app.quit()
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})