基于Electron的桌宠开发:集成Live2D、BongoCat与RPA的桌面自动化助手
2026/9/1 12:30:51 网站建设 项目流程

在桌宠开发中,把 Live2D、BongoCat、文本呈现框、五笔输入法和 RPA 组合到一起,听起来像一个大杂烩,但实际上是一条非常实用的桌面自动化产品化路径。很多开发者都想要一个能聊天、能响应按键、能执行系统指令的“电子老婆”或“办公助理”,但拆解下来,它其实就是“形象渲染 + 输入交互 + 自动化执行”三层结构的组合。本文会把这五个组件一一拆开,讲清楚每一层解决什么问题,然后给出一套基于 Electron 的桌宠原型搭建方案,最后讨论常见坑点和工程化建议。无论你是想做一个个人玩具,还是想给团队做一个桌面机器人入口,这篇文章的思路都可以直接复用。

1. 背景与核心概念

桌宠并不是一个新概念。从早期的“瑞星小狮子”到现在的 ASUS 天选姬、DeepSeek 桌宠插件,用户对“桌面上有一个能互动的虚拟角色”这件事一直有需求。早期桌宠大多是纯展示或简单待机动画,但现在的桌宠已经开始承担更实际的任务:接收用户的中文指令,调用自动化脚本,执行系统操作,把结果通过气泡文本反馈给用户。这就让桌宠从“玩具”变成了“桌面生产力入口”。

本文要搭建的桌宠正是这种形态。它的核心组成可以拆成五个部分:

  • Live2D:负责角色的身体、表情和动作,让桌宠有“形象”。
  • BongoCat:负责猫爪动画,模拟用户敲击键盘时桌宠跟随按键做出“打字”动作,让桌宠更生动。
  • 文本呈现框:负责显示对话气泡、状态提示、RPA 执行结果,让桌宠“会说话”。
  • 五笔输入法:是用户输入中文指令的通道。因为五笔输入法在电竞或办公场景中仍有大量用户,如果桌宠要支持这些用户,就不能假设大家都用拼音。
  • RPA:负责执行用户指令,比如打开软件、操作网页、整理文件、批量处理 Excel 等。桌宠只负责发指令,RPA 负责干重活。

把这五个部分放在一起,桌宠就从一个“只会卖萌的窗口”变成了“形象化的人机交互入口”。用户在桌宠上敲一行五笔打出的中文指令,桌宠把文字解析成结构化命令,再交给 RPA 去执行,最后把结果用气泡形式展示出来。这套链路听起来很复杂,但拆开看每一步都很清晰,而且每一步都有成熟的开源或商业方案可以选用。

阅读这篇文章的读者,建议至少了解 HTML/CSS/JavaScript 基础,知道 Electron 的基本用法,理解“进程与主进程通信”的概念。如果你完全没接触过 Electron,也不用担心,我会在实战部分把每一步都写清楚。至于 Live2D 模型、BongoCat 源码和 RPA 组件,我们会尽量使用通用接口,避免绑定某个特定产品。

2. 环境准备与版本说明

因为最终要搭建一个能运行在 Windows 桌面上的桌宠,我选择 Electron 作为应用外壳。Electron 的优点是可以直接用 Web 技术渲染 Live2D,同时又能调用 Node.js 能力,还可以嵌入任意 HTML 页面,非常契合“透明窗口 + 气泡 + 动画”的需求。

下面是我在本文实战环节所使用的环境,你可以根据自己的实际情况调整:

  • 操作系统:Windows 10/11(macOS / Linux 的透明窗口配置略有差异,但整体思路一致)
  • Node.js 16 及以上版本
  • npm 或 yarn 包管理器
  • Electron 22 及以上版本(建议 24+,后续示例代码默认使用较新的语法)
  • Live2D Cubism Web SDK 或 pixi-live2d-display 插件
  • BongoCat 开源项目(GitHub 上可以搜索到,不建议直接复制最新 release,应按你的 Electron 版本适配)
  • 任意一款支持五笔的输入法,比如搜狗五笔、QQ 五笔。需要注意 QQ 五笔在某些版本下会输入多余空格,需要在输入法设置里关闭“五笔兼容”相关选项。
  • 影刀 RPA(或其同类产品)作为可选自动化执行层。如果你不想安装商业软件,也可以使用 Python + pyautogui 代替,但就无法体验真正意义上的 RPA 流程编排。

需要说明的是,这些组件之间并没有强依赖关系。Live2D 只是渲染层,BongoCat 只是动画层,RPA 是独立服务。所以如果你在某个环节卡住了,可以先跳过它,不影响其他模块的调试。比如你先不想接 RPA,可以先用一个本地 mock 函数接收指令,等后面再接入真正的自动化执行。这也是我在实战部分推荐的开发顺序。

3. 核心语法、配置或原理拆解

这一节会围绕五个核心模块分别展开。每个模块我都会先说明它解决什么问题,再给出最小实现思路,最后补充关键参数和常见误区。这样在后面做完整桌宠时,你不会觉得代码是拼凑出来的。

3.1 Live2D 模型加载与渲染

Live2D 不是传统 3D 模型,而是一套基于纹理网格绑定的 2D 动画技术。它的文件结构一般包含三类内容:纹理图片(通常以.png形式存在)、模型定义文件(.model3.json)、以及让角色动起来的状态机或表情文件(.motions.expressions)。在 Web 环境下,最常用的加载方式有两种:一种是使用官方 Cubism Web SDK,另一种是使用开源社区封装的pixi-live2d-display

推荐优先尝试pixi-live2d-display。它基于 PixiJS 开发,你可以把 Live2D 模型当作一个 PixiJS 显示对象加入舞台,然后使用原生 PixiJS 的坐标、缩放、旋转来控制模型。下面是一个最简单的加载示例:

import { Live2DModel } from 'pixi-live2d-display'; import * as PIXI from 'pixi.js'; const app = new PIXI.Application({ view: document.getElementById('canvas'), autoStart: true, width: 300, height: 300, transparent: true }); const model = await Live2DModel.from('/models/your-model.model3.json'); app.stage.addChild(model); model.scale.set(0.25); model.anchor.set(0.5, 0.5);

这里的关键是Live2DModel.from()返回一个 Promise,所以要用await等待模型加载完成。scale控制模型大小,anchor控制原点位置。如果你在设置模型位置时发现模型不在预期区域,优先检查anchorscale。另外需要注意,模型文件不能放在项目根目录下直接引用,必须通过 devServer 或打包后的资源路径访问。

常见误区是模型加载后不显示或报 CORS 错误。这通常是因为本地加载.model3.json时路径配置不正确,或者 Web 服务器没有正确处理.moc3和纹理文件的 MIME 类型。在 Electron 中使用file://协议加载本地文件时会遇到安全限制,最简单的方式是把模型文件放到public目录,然后用相对路径加载。

3.2 BongoCat 的猫爪跟随逻辑

BongoCat 是一个很有趣的开源项目,它会在桌面显示一个猫咪和猫爪,猫爪会随着键盘按键做按压动画。这个项目本质上是一个“键盘事件 + 动画状态机”:监听keydownkeyup,根据按键编码(如KeyAKeyB)切换对应的猫爪贴图或骨架位置。

在桌宠里集成 BongoCat 时,不需要把整个桌面应用都搬过来。如果你想做得轻量,可以直接把 BongoCat 的源码作为一个子窗口加载,或者把它的动画逻辑抽象成一个组件放到主窗口里。我这里给出一个通用实现思路:假设猫爪由左右两个“爪子”组成,每个爪子有“按下”和“抬起”两个状态,通过 CSS 类切换动画。

const pressedKeys = new Set(); window.addEventListener('keydown', (e) => { pressedKeys.add(e.code); updateCatPaws(); }); window.addEventListener('keyup', (e) => { pressedKeys.delete(e.code); updateCatPaws(); }); function updateCatPaws() { const leftHasKey = pressedKeys.has('KeyA') || pressedKeys.has('KeyW') || pressedKeys.has('KeyS'); const rightHasKey = pressedKeys.has('KeyJ') || pressedKeys.has('KeyK') || pressedKeys.has('KeyL'); document.querySelector('.left-paw').classList.toggle('pressed', leftHasKey); document.querySelector('.right-paw').classList.toggle('pressed', rightHasKey); }

这里只是演示了按键状态到 UI 状态的映射。真实 BongoCat 项目还包含猫爪悬停、按压力度、3D 视角倾斜等效果,但核心逻辑不变:通过e.code判断按键位置,再控制对应猫爪的动画状态。如果你需要更自然的猫爪动画,建议直接 fork BongoCat 源码,把它内部的keyDown/keyUp事件与你的桌宠窗口共享。

要注意的是,Electron 的主窗口如果设置了focusable: falsetransparent: true,键盘事件可能无法正常捕获。所以必须让桌宠的可交互区域保持可聚焦,或者使用globalShortcut来监听全局按键。globalShortcut的好处是即使桌宠窗口不聚焦也能感知用户敲键盘,但缺点是如果用户的鼠标正在其他窗口输入文字,你的桌宠也会跟着“敲爪”,这可能会造成打扰。因此最好加一个开关,只在用户激活桌宠对话时开启猫爪同步。

3.3 文本呈现框的设计

文本呈现框就是桌宠的“嘴”。它要解决的问题是:把一段文字(比如欢迎语、指令回执、RPA 执行结果)用气泡形式显示在角色旁边。在 Electron 中,最直接的做法是用一个透明的BrowserWindow,里面放一个div,通过 CSS 实现圆角气泡、尾巴、阴影和淡入淡出效果。

气泡的样式需要和桌宠风格统一。我的建议是不要做得太复杂:白色半透明背景、圆角 12px、内边距 12px、最大宽度 240px、底部一个小箭头指向角色。这样的气泡在大多数 Live2D 角色旁边都很协调。为了支持多段文本排队显示,我们需要维护一个消息队列,每次只显示一条,等当前消息显示完或超时后显示下一条。

class BubbleQueue { constructor() { this.queue = []; this.isShowing = false; } push(text) { this.queue.push(text); this.showNext(); } async showNext() { if (this.isShowing || this.queue.length === 0) return; this.isShowing = true; const text = this.queue.shift(); document.getElementById('bubble').textContent = text; document.getElementById('bubble').classList.add('visible'); await wait(3000); document.getElementById('bubble').classList.remove('visible'); await wait(300); this.isShowing = false; this.showNext(); } }

这里的wait是对setTimeout封装的 Promise。使用队列可以避免消息相互覆盖,比如用户连续输入两条指令时,桌宠会依次展示结果而不是突然跳到最后一条。

除了对话气泡,文本呈现框也可以承担状态提示的功能。例如 RPA 正在执行时显示“正在打开计算器…”;执行完成显示“已完成”。在视觉上,可以用不同颜色区分“成功”“失败”“等待”。我通常会准备三个 CSS 类:.bubble-success.bubble-error.bubble-waiting,通过切换类名来表达状态。

3.4 五笔输入法与指令解析

很多桌宠教程默认使用拼音输入法,但实际工作中还有大量五笔用户。要让桌宠支持五笔输入法,并不是要你去解析五笔码表,而是要让桌宠的文本输入框兼容 Windows 的输入法框架。在 Electron 中,只要你在页面里放了一个<input><textarea>,用户使用任意输入法都可以正常输入。你需要处理的关键事件是compositionstartcompositionupdatecompositionend,它们标记了输入法组合输入的开始、更新和结束。

例如用户用五笔输入法打出“打开计算器”,这几个字会在组合输入过程中逐步进入<input>的 value。如果你监听keydown事件,只会拿到类似KeyDKeyK的物理按键码,无法拿到最终汉字。所以必须在compositionend事件里读取event.data,或者读取input.value来获得完整的汉语句子。

const commandInput = document.getElementById('command-input'); commandInput.addEventListener('compositionend', (e) => { const text = e.data || commandInput.value; if (text.trim()) { handleCommand(text.trim()); commandInput.value = ''; } }); commandInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) { const text = commandInput.value.trim(); if (text) { handleCommand(text); commandInput.value = ''; } } });

这里有两点特别容易踩坑。第一,五笔输入法在打单字时可能会触发keydown的 Enter 键,所以必须判断e.isComposing,否则会在候选词未确认时误提交指令。第二,QQ 五笔输入法在某些版本中会把“空格上屏”和普通空格混在一起,如果用户打完字后按空格选字,input.value里可能多出一个空格。所以拿到文本后要用.trim()去掉首尾空格。如果仍然出现多余空格,建议在输入法配置中关闭“允许空格选字”或“五笔拼音混输”相关选项。

通过这种方式,五笔输入法和拼音输入法在桌宠里就完全一致了。用户想要让桌宠执行自动化命令,只需要在输入框里用任意输入法打出中文句子即可。

3.5 RPA 动作执行原理

RPA(Robotic Process Automation)是机器人流程自动化的简称。它的目标是模拟人类在电脑上的操作,比如点击按钮、填写表单、处理文件、读取网页等。一个完整的 RPA 产品通常包含“流程编辑器”和“机器人执行器”。开发者在编辑器里编排流程,然后发布给机器人执行。

在桌宠场景里,RPA 充当的是“手脚”。桌宠需要把用户的中文指令转换成 RPA 能理解的“流程触发信号”。最稳妥的方式是让 RPA 服务监听本地 HTTP 端口,桌宠通过 HTTP 请求触发流程。比如我们把一个 RPA 流程命名为“打开计算器”,ID 为open_calc,那么桌宠就可以在指令解析后发送:

async function triggerRpa(processId, params = {}) { const response = await fetch('http://127.0.0.1:8080/rpa/run', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ processId, params, source: 'desktop-pet' }) }); if (!response.ok) throw new Error(`RPA 调用失败:${response.status}`); return await response.json(); }

不同 RPA 产品的 HTTP 接口格式不同,但思路相似。如果你使用影刀 RPA,可以查看官方文档中“外部调用流程”或“API 集成”的说明,按要求配置鉴权参数。如果不想依赖商业 RPA,也可以直接在 Node.js 进程里调用child_process.execFile执行 Python 脚本或命令行工具。这样同样能让桌宠完成“打开计算器”“查看天气”“抓取网页数据”等操作。

需要注意的是,RPA 本质上是自动化操控操作系统,权限很高。如果桌宠的指令解析做得太宽松,用户输入任意文字都可能触发危险操作,所以一定要在指令解析层增加白名单或确认机制。比如只有包含“计算器”“记事本”“截图”等预置命令时才触发 RPA;对于删除、格式化等危险操作,必须二次确认。

4. 完整实战:搭建一个桌宠原型

下面我们开始动手,把前面拆解的五个部分组合成一个最小可运行的原型。这个原型包含一个透明窗口、一个 Live2D 角色、一组猫爪动画、一个对话气泡、一个支持五笔输入的指令输入框,以及一个 mock 的 RPA 调用模块。

4.1 初始化项目结构与依赖

首先创建一个新目录,并初始化 npm 项目:

mkdir desktop-pet cd desktop-pet npm init -y

安装 Electron、PixiJS 和 pixi-live2d-display:

npm install electron pixi.js pixi-live2d-display

如果你的网络环境下载依赖较慢,可以配置国内镜像。接下来创建一个简单的项目目录,我会把所有的代码放在根目录,便于快速理解:

desktop-pet/ ├── main.js ├── preload.js ├── index.html ├── renderer.js ├── models/ │ └── your-model/ │ ├── your-model.model3.json │ ├── your-model.moc3 │ └── textures/ │ └── texture_00.png └── package.json

models/your-model/目录需要放入你自己的 Live2D 模型文件。如果你暂时没有模型,可以先去 Live2D 官网或开源社区下载免费模型导出为 v3 格式,这里只做演示,不限制具体模型。

4.2 创建 Electron 主进程

main.js负责创建桌宠窗口。为了实现透明背景、无边框、置顶显示,需要设置一些窗口参数。同时通过ipcMain.handle暴露一个rpa:run接口给渲染进程调用。

// main.js const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); function createWindow() { const win = new BrowserWindow({ width: 400, height: 500, transparent: true, frame: false, alwaysOnTop: true, resizable: false, hasShadow: false, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false } }); win.setAlwaysOnTop(true, 'screen-saver'); win.loadFile('index.html'); // 防止透明窗口在鼠标穿透时无法交互 win.setIgnoreMouseEvents(false); } app.whenReady().then(() => { createWindow(); app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit(); }); // 模拟 RPA 执行接口 ipcMain.handle('rpa:run', async (event, payload) => { console.log('收到 RPA 指令:', payload); const { processId } = payload; // 这里应该是真正调用 RPA 的代码 if (processId === 'open_calc') { return { success: true, message: '已在模拟环境中打开计算器' }; } return { success: false, message: '未识别的流程 ID' }; });

preload.js用来安全地暴露渲染进程可调用方法。因为我们在webPreferences中关闭了nodeIntegration,所以通过 contextBridge 暴露:

// preload.js const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('petAPI', { runRpa: (payload) => ipcRenderer.invoke('rpa:run', payload) });

4.3 编写渲染页面

index.html包含画布、输入框和气泡容器。为了让透明窗口不显示默认背景,我们整个页面使用透明背景。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>桌宠</title> <style> html, body { margin: 0; padding: 0; width: 100%; height: 100%; background: transparent; overflow: hidden; font-family: "Microsoft YaHei", sans-serif; } #stage { position: absolute; left: 50px; top: 30px; width: 300px; height: 300px; } canvas { display: block; } .paw { position: absolute; width: 40px; height: 40px; background-image: url('paw.png'); background-size: contain; transition: transform 0.05s linear; } #left-paw { left: 80px; bottom: 20px; } #right-paw { right: 80px; bottom: 20px; } .paw.pressed { transform: translateY(4px) scale(0.95); } #bubble { position: absolute; left: 140px; top: 10px; max-width: 220px; background: rgba(255, 255, 255, 0.9); border-radius: 12px; padding: 10px 14px; font-size: 14px; color: #333; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); opacity: 0; transition: opacity 0.3s; pointer-events: none; } #bubble.visible { opacity: 1; } #command-input { position: absolute; left: 20px; bottom: 70px; width: 240px; height: 28px; border: 1px solid #ccc; border-radius: 8px; padding: 4px 10px; font-size: 13px; background: rgba(255,255,255,0.85); outline: none; color: #333; } </style> </head> <body> <div id="stage"></div> <div id="left-paw" class="paw"></div> <div id="right-paw" class="paw"></div> <div id="bubble"></div> <input id="command-input" placeholder="输入指令(支持五笔输入)" /> <script src="renderer.js" type="module"></script> </body> </html>

这里的#stage用于挂载 PixiJS 画布,猫爪图片paw.png需要你自行准备一张猫爪素材。如果暂时没有,可以用一个圆形色块替代,重点看交互逻辑。

4.4 渲染 Live2D 模型

renderer.js中加载 Live2D 模型并挂到 PixiJS 舞台上。由于 PixiJS 和pixi-live2d-display的版本兼容性比较敏感,下面代码以当前常见 API 为例:

// renderer.js import * as PIXI from 'pixi.js'; import { Live2DModel } from 'pixi-live2d-display'; const app = new PIXI.Application({ view: document.getElementById('stage'), autoStart: true, resizeTo: document.getElementById('stage'), backgroundAlpha: 0 }); async function loadModel() { try { const model = await Live2DModel.from('/models/your-model/your-model.model3.json'); app.stage.addChild(model); model.scale.set(0.2); model.position.set(150, 250); model.anchor.set(0.5, 0.5); } catch (err) { console.error('Live2D 模型加载失败:', err); document.getElementById('bubble').textContent = '模型加载失败,请检查路径'; document.getElementById('bubble').classList.add('visible'); } } loadModel();

backgroundAlpha: 0是为了让 PixiJS 画布背景透明,这样才能和 Electron 透明窗口融合。scaleposition需要根据你实际模型大小调整。如果你发现模型太大或太小,可以直接改这两个值。

4.5 集成猫爪动画

在同一个renderer.js中,我们监听全局键盘事件,触发猫爪按压状态。

const leftPaw = document.getElementById('left-paw'); const rightPaw = document.getElementById('right-paw'); const pressedKeys = new Set(); const leftKeys = ['KeyA', 'KeyS', 'KeyD', 'KeyW']; const rightKeys = ['KeyJ', 'KeyK', 'KeyL', 'KeyI']; window.addEventListener('keydown', (e) => { pressedKeys.add(e.code); updatePaws(); }); window.addEventListener('keyup', (e) => { pressedKeys.delete(e.code); updatePaws(); }); function updatePaws() { const leftPressed = leftKeys.some((key) => pressedKeys.has(key)); const rightPressed = rightKeys.some((key) => pressedKeys.has(key)); leftPaw.classList.toggle('pressed', leftPressed); rightPaw.classList.toggle('pressed', rightPressed); }

注意这里window.addEventListener监听的是渲染进程内的键盘事件。如果输入框当前有焦点,键盘事件会先被输入框处理,但事件仍然会冒泡到window,所以猫爪依然能响应。不过当用户激活其他应用时,桌宠窗口失焦,window将收不到事件。如果需要全系统捕捉按键,应使用globalShortcut,并在主进程中通过 IPC 通知渲染进程。

4.6 添加文本呈现框逻辑

消息队列已经在原理部分讲过,现在把它写成一个可复用的模块。

function wait(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } class BubbleQueue { constructor() { this.queue = []; this.isShowing = false; this.bubbleElement = document.getElementById('bubble'); } push(text, type = 'normal') { this.queue.push({ text, type }); this.showNext(); } async showNext() { if (this.isShowing || this.queue.length === 0) return; this.isShowing = true; const { text, type } = this.queue.shift(); this.bubbleElement.textContent = text; this.bubbleElement.className = 'visible ' + type; await wait(2500); this.bubbleElement.className = ''; await wait(300); this.isShowing = false; this.showNext(); } } const bubble = new BubbleQueue();

使用类型控制颜色,可以再补充三个样式类:

.bubble.success { border-left: 4px solid #52c41a; } .bubble.error { border-left: 4px solid #ff4d4f; } .bubble.waiting { border-left: 4px solid #faad14; }

4.7 五笔输入框与指令解析

我们继续在renderer.js里绑定输入框的compositionend事件。当用户用五笔输入法打出完整的一句话后,这句话会被交给handleCommand

const commandInput = document.getElementById('command-input'); commandInput.addEventListener('compositionend', (e) => { const text = e.data || commandInput.value; if (text.trim()) { handleCommand(text.trim()); commandInput.value = ''; } }); commandInput.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) { const text = commandInput.value.trim(); if (text) { handleCommand(text); commandInput.value = ''; } } }); async function handleCommand(text) { bubble.push(`你输入了:${text}`, 'waiting'); // 非常简单的指令映射,实际项目建议用 NLP 或正则 const ruleMap = [ { pattern: /^(打开|启动)(计算器)$/i, processId: 'open_calc' }, { pattern: /^(打开|启动)(记事本)$/i, processId: 'open_notepad' } ]; for (const rule of ruleMap) { const match = text.match(rule.pattern); if (match) { try { const result = await window.petAPI.runRpa({ processId: rule.processId, rawText: text }); if (result.success) { bubble.push(result.message, 'success'); } else { bubble.push(`执行失败:${result.message}`, 'error'); } } catch (err) { bubble.push(`调用 RPA 出错:${err.message}`, 'error'); } return; } } bubble.push('抱歉,我不知道该怎么做这个操作。', 'normal'); }

这里我使用正则来做简单的指令匹配。match结果只在需要保留分组时使用,示例里只判断是否匹配。为了演示,RPA 调用走了preload.js暴露的window.petAPI.runRpa,返回结果后通过气泡反馈。

4.8 运行与验证

package.json中配置启动命令:

{ "scripts": { "start": "electron ." } }

然后在项目根目录执行:

npm start

如果一切正常,你会看到一个透明背景的桌宠窗口,Live2D 角色显示在窗口左侧,猫爪图片显示在底部。点击输入框,用任意输入法(包括五笔)输入“打开计算器”,回车后气泡会显示“正在执行…”,随后收到模拟的 success 消息。

如果你的 RPA 服务已经真实启动,可以把ipcMain.handle里的模拟逻辑替换为真实的 HTTP 请求调用,这样桌宠就真正连上了 RPA。到此,五个组件已经完成闭环。

5. 常见问题与排查思路

在实际搭建过程中,下面的问题出现频率最高,我把现象、原因和解决办法整理成一张表,方便你快速定位。

问题现象常见原因解决思路
Live2D 模型加载后不显示.model3.json路径错误或scale设置过小在浏览器控制台查看网络请求,确认模型文件是否 200;调整scale到 0.3~0.5 再观察
猫爪动画长时间无反应窗口失焦导致键盘事件未进入渲染进程改用globalShortcut监听全局键盘,通过 IPC 通知渲染进程更新猫爪
输入框用五笔输入后触发异常未处理compositionend,在组字过程中读取了keydown增加isComposing判断;在compositionend后再读取文本
QQ 五笔输入后结果带空格输入法“空格选字/上屏”与常规空格冲突在输入法设置中关闭“空格上屏”,或代码中对文本trim()并替换\u00A0
透明窗口无法点击setIgnoreMouseEvents(true)被误设置检查主进程中是否调用了setIgnoreMouseEvents(false)
RPA 流程触发后无反应流程 ID 错误或 RPA 服务未启动先手动调用 HTTP 接口测试;确认使用正确的鉴权 token
文字气泡遮挡过多画面气泡显示时间过长或优先级过高缩短wait时间;气泡最大宽度改小;增加手动关闭按钮

除了表格里的问题,还有两个非常隐蔽的坑。第一,Electron 透明窗口开启后,如果页面里的元素有抗锯齿透明渐变,可能会出现黑色或白色边缘,建议给容器设置background: transparent而不是rgba(0,0,0,0),同时不要在html上强制设置背景色。第二,Live2D 模型依赖 WebGL 上下文,如果桌面启用了某些远程桌面会禁用 WebGL,模型会加载失败,这种情况只能在本地主机上运行,或者切换到 Canvas2D 渲染模式(但效果会打折)。

6. 最佳实践与工程建议

当原型跑通后,下一步就是把它工程化。这里分享我在桌宠项目里沉淀下来的几条经验。

6.1 把指令解析从桌宠逻辑中抽离

上面示例把指令映射直接写在renderer.js里,适合 demo,但不利于维护。更合理的做法是把指令解析单独放到一个模块里,使用 DSL 或 JSON 规则描述“说什么做什么”。比如command-rules.json

[ { "name": "打开计算器", "keywords": ["打开计算器", "启动计算器"], "processId": "open_calc" }, { "name": "打开记事本", "keywords": ["打开记事本", "记事本"], "processId": "open_notepad" } ]

然后在代码里遍历规则,匹配到任意关键词就执行对应processId。这样新增一个指令只需要改 JSON,不用动代码。

6.2 使用 IPC 做清晰的消息边界

桌宠的渲染进程不应该直接调用 Node.js 的child_process,否则一旦页面被 XSS 攻击,攻击者就能拿到系统控制权。所以必须通过contextBridge暴露最小 API,让渲染进程只能调runRpa,不能执行任意 shell 命令。主进程的ipcMain.handle里还应该做参数校验,比如只允许特定字段长度和类型。对于危险 RPA 流程,应当加二次确认提示。

6.3 注意 RPA 权限与防误触

RPA 流程本质上是在操作系统上模拟鼠标键盘,如果指令映射写得太宽,用户无意中一句话就可能触发删除文件等操作。建议把 RPA 流程分为三类:低危(打开应用、显示系统信息)、中危(读取文件、网页操作)、高危(删除文件、执行脚本)。低危流程可以直接执行,中危需要弹气泡确认,高危流程必须输入二次确认口令,并且记录操作日志。

6.4 使用配置中心管理模型与流程

桌宠模型路径、RPA 端点地址、气泡样式这些信息不要硬编码在项目里。可以把它们写到一个config.json文件,在主进程启动时读取。不同电脑上可以通过环境变量覆盖。例如:

{ "modelPath": "/models/your-model/your-model.model3.json", "rpaEndpoint": "http://127.0.0.1:8080/rpa/run", "rpaToken": "", "bubbleTimeout": 2500 }

这样在没有 RPA 服务的开发环境中,也可以把rpaEndpoint指向一个 mock server,方便前端独立调试。

6.5 性能优化:减少不必要的重绘

Live2D 模型每帧都在重绘,猫爪动画也依赖 CSS 切换。如果桌宠窗口一直置顶且不处理失焦状态,会持续消耗 CPU 和 GPU。建议在桌宠失焦时暂停模型的 idle 动画,只在用户触发输入或 RPA 回调时才唤醒。也可以监控document.hidden状态,在窗口不可见时降低帧率。

6.6 打包分发

最后,桌宠应用如果只是自己玩,npm start就够了。但如果想分发给其他用户,需要使用electron-builderelectron-packager打包。打包时要注意 Live2D 模型文件可能很大,需要把models目录一起打包。对于 Windows 平台,建议使用 NSIS 安装包模式,并勾选“开机自启”选项。这样桌宠才能像一个真正的桌面应用一样常驻。

7. 总结与下一步学习路线

本文从桌宠的整体架构出发,拆解了 Live2D、BongoCat、文本呈现框、五笔输入法和 RPA 五个组件的职责与实现方式,并给出了一套基于 Electron 的桌宠原型。通过这套代码,你可以快速拥有一个会动、会打字、会说话、能接 RPA 的桌面角色。

接下来可以继续深入的方向有三个:第一,把指令解析换成真正的意图识别服务,比如接入本地大模型或开源 NLP 工具,让桌宠能听懂更自然的语言;第二,把 Live2D 模型和 BongoCat 动画结合得更紧密,让猫爪“长”在角色身上而不是悬浮在角色旁边;第三,把 RPA 流程做成可视化任务库,让用户不在代码里新增指令,而是通过界面配置就能扩展能力。

桌宠这条路并不难,难的是把每个模块的边界理清楚。如果你按本文的顺序从窗口搭建开始,逐步添加动画、输入、自动化,你会发现自己对 Electron、输入法事件和 RPA 的理解都变得清晰了很多。动手跑一遍上面的 demo,再改成你自己的模型和指令,一个真正属于你的桌面宠物就算落地了。希望这篇文章能帮你少踩一些坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询