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

server/2025/1/15 16:51:48/

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/server/158606.html

相关文章

fpga系列 HDL:跨时钟域同步 双触发器同步器

目录 **双触发器同步器&#xff08;Two-Flip-Flop Synchronizer&#xff09;示例代码**&#xff1a;双触发器同步器的优缺点优点&#xff1a;缺点&#xff1a;适用场景&#xff1a; 应用实例&#xff1a;同步来自spi_slave的单个使能信号 跨时钟域的设计需要特别小心&#xff0…

快速上手Git——Windows系统下Git的安装与简单使用流程

一、Git的下载和安装 Git官网链接&#xff1a;https://git-scm.com/ 进入官网后选择Downloads 选择与系统相符合的版本下载&#xff0c;这里我使用的是windows系统 然后点击下载 根据流程安装完成后&#xff0c;使用以下命令查看git版本 git -v运行结果&#xff1a; 二、…

PID控制器 (Proportional-Integral-Derivative Controller) 算法详解及案例分析

PID控制器 (Proportional-Integral-Derivative Controller) 算法详解及案例分析 目录 PID控制器 (Proportional-Integral-Derivative Controller) 算法详解及案例分析1. 引言2. PID控制器的基本概念2.1 PID控制器的定义2.2 PID控制器的核心思想2.3 PID控制器的应用领域 3. PID控…

【Rust】结构体示例与调试

目录 思维导图 1. 引言 2. 初始程序示例 3. 使用元组重构 4. 使用结构体重构 5. 调试与输出 6. 进一步的功能扩展 思维导图 1. 引言 本文通过一个计算矩形面积的程序&#xff0c;展示了如何从简单变量逐步重构到使用结构体&#xff0c;以提高代码的清晰度和可管理性。结…

微信小程序获取当前页面路径,登录成功后重定向回原页面

&#x1f935; 作者&#xff1a;coderYYY &#x1f9d1; 个人简介&#xff1a;前端程序媛&#xff0c;目前主攻web前端&#xff0c;后端辅助&#xff0c;其他技术知识也会偶尔分享&#x1f340;欢迎和我一起交流&#xff01;&#x1f680;&#xff08;评论和私信一般会回&#…

java流式处理zip+多线程

概述 流式处理一个zip&#xff0c;zip里有多个json文件。 流式处理可以避免解压一个大的zip。再加上多线程&#xff0c;处理的效率杠杠的。 代码 package 多线程.demo05多jsonCountDownLatch;import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThro…

Python爬虫-爬取汽车之家全部汽车品牌的brandid(品牌ID)

前言 本文是该专栏的第42篇,后面会持续分享python爬虫干货知识,记得关注。 本文以汽车之家平台为例子,获取所有汽车品牌的“全部品牌ID”,即brandid数据。如下所示: 具体的实现思路以及完整实现代码逻辑,笔者将在正文进行详细介绍。废话不多说,跟着笔者直接往下看正文详…

Mycat读写分离搭建及配置超详细!!!

目录 一、Mycat产生背景二、Mycat介绍三、Mycat安装四、Mycat搭建读写分离1、 搭建MySQL数据库主从复制2、 基于mysql主从复制搭建MyCat读写分离 五、Mycat启动常见错误处理1、Caused by: io.mycat.config.util.ConfigException: SelfCheck### schema TESTDB refered by user u…