Electron 实现自定义弹窗通知

概述

Electron 是一个流行的开源框架,允许开发者使用 web 技术(如 HTML, CSS, 和 JavaScript)构建跨平台的桌面应用程序。内置的 Notification API 允许开发者轻松地向用户展示系统通知,但有时标准的通知样式可能不足以满足应用的特定需求或品牌要求。本文档将指导你如何在 Electron 应用中实现自定义的通知弹窗。

准备工作

确保你已经安装了 Electron 并创建了一个基础的 Electron 应用。如果你尚未开始,可以通过访问 Electron 官方网站获取快速上手指南。

使用原生 Notification API

基础用法

首先,了解如何使用 Electron 提供的基础 Notification API 是很重要的。这是一个简单的示例:

const { Notification } = require('electron');

let notification = new Notification({
    title: '基本通知',
    body: '这是一条普通的系统通知。'
});

notification.show();

自定义选项

虽然 Notification API 支持一些自定义,比如图标、声音等,但它主要遵循操作系统的通知样式。为了实现更深度的自定义,我们需要采用不同的策略。

实现自定义通知窗口

由于 Electron 的 Notification API 受限于系统样式,对于高度自定义的通知,我们可以创建一个自定义的 BrowserWindow 作为通知窗口。

为了让我们开发过程更加顺畅,我们预想一下我们想要实现的最终呈现效果:

1.png

创建自定义通知窗口

const { BrowserWindow } = require('electron');
let notificationWindow

// 自定义通知窗口
const createCustomNotificationWindow = () => {
    notificationWindow = new BrowserWindow({
        width: 350,
        height: 400,
        parent: win, // 选择父元素,使自定义窗口与父窗口附着在一个窗口
        frame: false, // 隐藏窗口边框
        transparent: true, // 设置为透明窗口(可能需要配合CSS)
        show: false, // 初始时不显示窗口
        alwaysOnTop: true, // 始终置顶
        resizable: false, // 禁止调整窗口大小
        webPreferences: {
            nodeIntegration: true, // 允许渲染器进程使用Node.js
            contextIsolation: false,
            autofill: true,
        },
    })

    // 添加通知消息
    ipcMain.on('add-notification', (event, data) => {
        isNull = false
        notificationWindow.show()
        notificationWindow.webContents.send('add-notification-item', data);
    });

    // 关闭通知窗口
    ipcMain.on('hide-notification', (event) => {
        isNull = true
        notificationWindow.hide()
    });

    // 加载自定义样式文件
    notificationWindow.loadFile('./electron/notification.html') // 加载自定义的HTML文件
    // 在这里设置窗口的位置,比如右下角
    notificationWindow.setPosition(screen.getPrimaryDisplay().workArea.width - 360, screen.getPrimaryDisplay().workArea.height - 410)
}

注意

这里选择在父窗口创建时,同时创建子窗口。

app.whenReady().then(() => {
    ipcMain.handle('ping', () => 'pong')
    // 父窗口
    createWindow()
    // 自定义通知窗口
    createCustomNotificationWindow()
})

并在你需要的时候调用notificationWindow.show()展示弹窗。

自定义通知界面 (notification.html)

在上面的代码中,我们加载了一个名为 notification.html 的文件,这个文件应包含你想要的自定义通知UI。例如:

首先让我们实现自定义通知的静态展示:

库引入

因为很少使用html原生开发自定义样式界面,所以我还是习惯使用vue+element开发。于是,我们通过引入的方式将需要用到的库全部写了进来。

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入Vue3 -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <!-- 引入element-plus样式文件 -->
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css" />
    <!-- 引入element-plus组件库 -->
    <script src="https://unpkg.com/element-plus"></script>
    <!-- 引入axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
</html>

自定义通知样式

<html lang="en">
  <body>
    <div id="app">
      <div class="notification-index">
        <el-button type="info" class="notification-btn" @click="onIgnoreAll">忽略全部</el-button>
        <div class="notification-cell">
          <transition-group name="fade" tag="p">
            <div v-for="item in notificationList" :key="item.id" class="cell-item" @mouseenter="pauseDeleting"
              @mouseleave="resumeDeleting" @click="handleClickMessageItem(item)">
              <el-button class="item-close-btn" text
                @click.prevent.stop="handleRemoveItem(item)">x</el-button>
              <div class="item-header">
                <img class="header-img" src="../src/assets/img/logo.png" alt="">
                <span class="header-title">{{ item.title }}</span>
              </div>
              <div class="item-content">
                {{ item.body }}
              </div>
            </div>
          </transition-group>
        </div>
        <audio id="myAudio" preload="auto">
          <source src="../audio/notification.mp3" type="audio/mpeg">
        </audio>
      </div>
    </div>
  </body>
</html>
<style>
  body {
    overflow: hidden;
  }

  .notification-index {
    display: flex;
    flex-direction: column;
    width: 338px;
    height: 400px;
    overflow: hidden;
  }

  .notification-cell {
    position: relative;
    width: 100%;
    height: calc(100% - 66px);
    overflow: auto;
  }

  .cell-item {
    position: relative;
    margin-bottom: 8px;
    border-radius: 4px;
    padding: 8px;
    height: 88px;
    background-color: #f1f1f1;
    cursor: pointer;
  }

  .cell-item:hover {
    transform: scale(0.99);
  }

  .item-close-btn {
    font-size: 14px;
    color: #000;
    position: absolute;
    right: 0;
    top: 0;
  }

  .item-header {
    display: flex;
    align-items: center;
  }

  .header-img {
    height: 16px;
    width: 16px;
  }

  .header-title {
    margin-left: 4px;
    font-size: 12px;
  }

  .item-content {
    margin-top: 12px;
    font-size: 14px;
    color: #000;
    word-break: break-all;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .notification-btn {
    height: 50px;
    width: 100%;
    background-color: #f1f1f1;
    border: none;
    border-radius: 4px;
    color: #000;
    border: 1px solid #f1f1f1;
  }

  /* 自定义整个滚动条 */
  ::-webkit-scrollbar {
    display: none;
  }

  .fade-enter-active,
  .fade-leave-active {
    transition: opacity 0.5s;
  }

  .fade-enter,
  .fade-leave-to {
    opacity: 0;
  }
</style>

事件逻辑实现

<script>
  const { createApp, ref, onMounted, onUpdated } = Vue;
  // 使用 setup 函数的 Vue 组件
  createApp({
    setup() {
      // 响应式状态
      const notificationList = ref([
        {
          title: 'xx通知',
          body: '演唱会结束以后,体育馆门口聚集了大量的歌迷,他们在彼此分享会唱的歌曲。。'
        },
        {
          title: 'xx通知',
          body: '学校组织的特色主题活动上,有很多学生聚集在一块。'
        },
        {
          title: 'xx通知',
          body: 'xxx'
        }
      ]);

      // 暴露给模板的响应式属性和方法
      return {
        notificationList
      };
    },
  }).use(ElementPlus).mount('#app');
</script>

至此你能得到一个自定义样式的静态通知弹窗,如图:

2.png

接下来只需要实现消息动态化就行了。。。

结论

通过上述步骤,你可以实现在 Electron 应用中展示自定义样式的弹窗通知。这种方法提供了极大的灵活性,使得通知能够更好地与应用的品牌风格保持一致,同时也能满足特殊的功能需求。记得在开发过程中充分测试不同平台下的兼容性和用户体验。

上一篇
快问快答
Embedding 和 Rerank 的核心知识点 Q1:Embedding 和 Rerank 的区别是什么? Embedding 用 Bi-Encoder 双塔架构做召回,Query 和文档独立编码成向量,速度快但精度一般。 Rerank 用 Cross-Encoder 交叉编码器做精排,Quer ....
下一篇
傻逼领导干大事
关于今天上午的傻逼领导操作,让人觉得这个世界简直到处都是傻逼。 前情提要:我们公司就是一个小破公司,在4月3日开了一个近期事项安排会议,把特种设备资料上传的任务交代给了相关人员了,其中包含了一个我们老板的心尖宠(非常信任的一个技术,就在昨天我得知心尖宠准备离职了)。 昨天下午技术经理突然给办公室经理 ....
时间排序
【认真摸鱼】试着做了几个鱼排的界面 随便写写 3 新人报到啦 ~~ 深度解析:Codex Pet Skill -- 从娱乐项目学习如何写 skill 傻逼领导干大事 Electron 实现自定义弹窗通知 快问快答 【小说】天眼 第十二章:成仙 + 第十三章(最终章):尾声 第六章 妥协 同城广播:本地鱼油扩列 随便写写 2
上一篇
Electron 搭建桌面端应用
前言 本文档主要介绍如何使用electron+vite+vue3+electron-forge来构建桌面应用程序。 Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron-forge是一个处理 Electron 应用程序打包与分发的一体化工 ....
下一篇
没有更多了
当前作者
Electron 实现自定义弹窗通知 Electron 搭建桌面端应用
上一篇
第八章 净火
霜气凝在中央广场的黑曜石地砖上,靴底踩上去有细微的碎裂声。埃拉不知道天气究竟有多冷,她的四肢百骸都像被冻进了深井。思绪迟缓、凝滞,理智一遍遍催促她该默念流程、该调整呼吸,但她只觉得冷。冷得骨头缝里都透着寒气,冷得连肺叶扩张都带着冰碴的刮擦感。 “埃拉·梵斯司铎阁下,时辰到了。” 身后的黑甲护卫低声催 ....
下一篇
【小说】天眼 第十一章:师傅
吴先生似完全没了生机,连天眼也没再看到气息流转。林昭像是遭受了极大的打击,默默将吴先生轻轻靠墙放好,站起身来。 “喂!这跟你说的不同!!”林昭对着空旷的房间大喊,“骗子!!骗子!!!”她将手上金光衰退显出黄纸原型的符箓狠狠摔在地上,猛地踩了几脚,飞溅起各种瓦砾碎石,发出极大的声响。 “唉……”一个雌 ....
热门排序
第十章 第一个世界收尾 新年伊始,你在战斗(十六) 第九章 结盟 【小说】某种超能力 - 第三十三章 抉择时刻 第八章 净火 Electron 实现自定义弹窗通知 【小说】天眼 第十一章:师傅 第五章 母本 第四章 初见男主 第二章:底稿 第一章:霜业城