electron编写一个macOS风格的桌面应用

devtools/2025/1/18 4:48:29/

electronmacOS_0">electron编写一个macOS风格的桌面应用

基于vue3+vite,看一下最后的效果:

image-20250114111720892

针对原始的electron模板,做了如下几点调整:

  • 背景边框进行了圆角处理
  • 隐藏了原始的titleBar
  • 增加了macOS风格的窗口管理工具,就是交通灯按钮组实现最大化/最小化/还原、关闭,并增加按钮点击事件

一、背景边框圆角处理

electron并不支持对桌面应用进行圆角处理,需要相对较为复杂的逻辑处理,思路如下:

  1. 主进程背景设置透明
  2. 渲染进程的入口组件设置背景圆角
  3. 入口页面隐藏滚动条

主进程中的核心代码:

javascript">// main.js
const { app, BrowserWindow } = require('electron');app.whenReady().then(() => {const mainWindow = new BrowserWindow({width: 800,height: 600,frame: false, // 移除默认框架transparent: true, // 使窗口背景透明webPreferences: {preload: path.join(__dirname, 'preload.js'),contextIsolation: true,},});mainWindow.loadURL('http://localhost:3000'); // 加载Vue应用或HTML文件
});

入口组件中的核心代码:

// App.vue
<template><div class="mainWin"><RouterView /></div>
</template><script setup>
import { RouterView } from 'vue-router';
import { ref } from 'vue';
</script><style lang="scss" scoped>
.mainWin {width: 100%;height: 100vh;background-color: #fff;border-radius: 5px;overflow-y: hidden;display: flex;flex-direction: column;
}</style>

入口页面文件中的核心代码:

<!doctype html>
<html><head><meta charset="UTF-8" /><title>Electron</title><!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --><!-- <metahttp-equiv="Content-Security-Policy"content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"/> --><style>body,html {overflow: hidden;border-radius: 5px;}</style></head><body><div id="app"></div><script type="module" src="/src/main.js"></script></body>
</html>

overflow: hidden;是用于隐藏滚动条,经测试,写在index.html文件中才生效,边框圆角写在app组件中生效

二、隐藏原始titleBar,增加交通灯按钮

1、隐藏titleBar

主进程中设置titleBarStyle: ‘hidden’,即可

如下:

javascript">function createWindow() {// Create the browser window.const mainWindow = new BrowserWindow({width: 900,height: 670,show: false,autoHideMenuBar: true,titleBarStyle: 'hidden',transparent: true,frame: process.platform === 'darwin',...(process.platform === 'linux' ? { icon } : {}),webPreferences: {preload: join(__dirname, '../preload/index.js'),sandbox: false}}).....省略其他代码
}

2、交通灯样式

逻辑是在app组件中添加header标签,添加按钮组,并设置样式

// App.vue
<script setup>
import { RouterView } from 'vue-router'
import { ref } from 'vue'// 方法用于最小化、关闭窗口以及切换最大化/还原状态
const isMaximized = ref(false)
const minimizeWindow = () => {if (window.api.platform !== 'darwin') {window.api.minimize()}
}const maximizeWindow = () => {if (isMaximized.value) {window.api.unmaximize()} else {window.api.maximize()}isMaximized.value = !isMaximized.value
}const closeWindow = () => {window.api.close()
}const toggleMaximize = () => {window.api.toggleMaximize()
}
</script><template><div class="mainWin"><header class="window-header" @dblclick="toggleMaximize"><!-- <span class="title">My App</span> --><div class="controls"><!-- 自定义交通灯按钮 --><button@click="minimizeWindow"class="traffic-light":class="'minimize'"title="最小化"></button><button@click="maximizeWindow"class="traffic-light":class="isMaximized ? 'restore' : 'maximize'":title="isMaximized ? '还原' : '最大化'"></button><button @click="closeWindow" class="traffic-light" :class="'close'" title="关闭"></button></div></header><RouterView /></div>
</template><style lang="scss" scoped>
.mainWin {/* 添加背景图片 */background-image: url('./assets/imgs/background.jpg');backdrop-filter: blur(10px); /* 模糊背景 */background-size: cover; /* 自适应填充 */background-position: center; /* 居中显示 */background-repeat: no-repeat; /* 避免重复 */width: 100%;height: 100vh;// margin: 0;// padding: 0;background-color: #fff;border-radius: 5px;// overflow-y: hidden;.window-header {-webkit-app-region: drag;display: flex;justify-content: space-between;align-items: center;padding: 10px;background-color: #f0f0f0;border-bottom: 1px solid #ddd;border-top-left-radius: 5px;border-top-right-radius: 5px;flex-shrink: 0;}.controls {display: flex;align-items: center;}.traffic-light {-webkit-app-region: no-drag;width: 12px;height: 12px;margin-left: 8px;border-radius: 50%;background-color: transparent;border: none;cursor: pointer;transition: all 0.2s ease;&:hover {transform: scale(1.2);}&.minimize {background-color: #ffcc00; // 黄色}&.maximize {background-color: #9fac00; // 绿色}&.restore {background-color: #9fac00; // 恢复按钮也用绿色}&.close {background-color: #ff4f38; // 红色}}.title {-webkit-app-region: drag;font-size: 14px;font-weight: bold;}
}
</style>

3、交通灯按钮组点击响应逻辑

窗口的最大最小化以及还原操作需要调用window底层api,因此需要渲染进程和主进程进行通讯,逻辑如下:

  1. 渲染进程点击对应的按钮,就是上面代码中的点击事件

  2. 在预加载进程中分别添加最大化、最小化、还原、关闭以及拖动窗口的api,同时通过ipcRenderer向主进程发送触发信号

    javascript">// preload/index.js
    import { contextBridge, ipcRenderer } from 'electron'
    import { electronAPI } from '@electron-toolkit/preload'// Custom APIs for renderer
    const api = {// 添加一个方法来打开新窗口openNewWindow: async () => {try {const response = await ipcRenderer.invoke('open-new-window')return response} catch (error) {console.error('Failed to open new window:', error)throw error}},// 窗口变化的apiminimize: () => ipcRenderer.send('minimize-window'),maximize: () => ipcRenderer.send('maximize-window'),unmaximize: () => ipcRenderer.send('unmaximize-window'),close: () => ipcRenderer.send('close-window'),toggleMaximize: () => ipcRenderer.send('toggle-maximize'),platform: process.platform,
    }// Use `contextBridge` APIs to expose Electron APIs to
    // renderer only if context isolation is enabled, otherwise
    // just add to the DOM global.
    if (process.contextIsolated) {try {contextBridge.exposeInMainWorld('electron', electronAPI)contextBridge.exposeInMainWorld('api', api)} catch (error) {console.error(error)}
    } else {window.electron = electronAPIwindow.api = api
    }
    

    platform: process.platform,是为了在渲染进程中可以获取到操作系统的信息

  3. 主进程通过ipcMain接收指令,并对窗口进行对应操作

    javascript">ipcMain.on('minimize-window', () => {mainWindow.minimize()})ipcMain.on('maximize-window', () => {mainWindow.maximize()})ipcMain.on('unmaximize-window', () => {mainWindow.unmaximize()})ipcMain.on('close-window', () => {mainWindow.close()})ipcMain.on('toggle-maximize', () => {if (mainWindow.isMaximized()) {mainWindow.unmaximize()} else {mainWindow.maximize()}})
    

http://www.ppmy.cn/devtools/151094.html

相关文章

api开发及运用小红书笔记详情api如何获取笔记详情信息

item_get_video-获得某书笔记详情 smallredbook.item_get_video 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,i…

RPC 源码解析~Apache Dubbo

解析 RPC&#xff08;远程过程调用&#xff09;的源码可以帮助你深入理解其工作原理和实现细节。为了更好地进行源码解析&#xff0c;我们选择一个流行的 RPC 框架——Apache Dubbo 作为示例。Dubbo 是一个高性能、轻量级的开源 Java RPC 框架&#xff0c;广泛应用于企业级应用…

机器学习在服务监控中的创新应用:提升运维效率与可靠性

《机器学习在服务监控中的创新应用:提升运维效率与可靠性》 一、引言 在当今复杂的信息技术环境中,服务监控对于确保系统的稳定运行至关重要。传统的服务监控方法往往依赖于预定义的阈值和规则,但在面对复杂多变的服务行为时,这些方法可能会显得力不从心。机器学习的出现…

AI知识-TF-IDF技术(Term Frequency-Inverse Document Frequency)

摘要 TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种常见的统计方法&#xff0c;用于评估一个词对于一个文档集或一个语料库中的其中一份文档的重要性。本文将全面阐述TF-IDF的通俗理解、技术原理、应用场景&#xff0c;并做以总结。 通俗理…

《OpenCV》——模版匹配

文章目录 OpenCV——模版匹配简介模版匹配使用场景OpenCV 中模板匹配的函数参数 OpenCV——模版匹配实例导入所需库读取图片并处理图片对模版图片进行处理进行模版匹配显示模版匹配的结果注意事项 OpenCV——模版匹配简介 OpenCV 是一个非常强大的计算机视觉库&#xff0c;其中…

Spring Boot 3.x 整合 Logback 日志框架(支持异步写入)

Spring Boot 3.x 整合 Logback 日志框架&#xff08;支持异步写入&#xff09; 在构建任何应用程序时&#xff0c;良好的日志管理都是必不可少的。日志可以帮助我们监控、调试和跟踪代码的运行情况。 1. 添加日志配置文件 在 /resources 资源目录下&#xff0c;创建名为 log…

Vue.js 组件的基本结构:模板、脚本和样式

Vue.js 组件的基本结构&#xff1a;模板、脚本和样式 大家好&#xff01;今天我们聊聊 Vue 组件的基本结构。如果你刚接触 Vue.js&#xff0c;可能会觉得 .vue 文件有点特殊。其实&#xff0c;Vue 组件是高度模块化的&#xff0c;分为三部分&#xff1a;模板、脚本 和 样式。接…

【PHP】双方接口通信校验服务

请求方 使用 ApiAuthService::buildUrl($domain, [terminal => 1, ts => time()]); //http://域名/adminapi/login/platformLogin?sign=F7FE8A150DEC18BE8A71C5059742C81A&terminal=1&ts=1736904841接收方 $getParams = $this->request->get();$validate…