原创 zlmic 2025-05-15 08:31 重庆
点击关注公众号,“技术干货” 及时达!
点击关注公众号,“技术干货” 及时达!
当二次元遇上生产力工具
最近开发了一款名为"小桌宠"的跨平台桌面应用,将3D宠物与任务管理结合。这只兔子不仅能卖萌,还会在你摸鱼时弹出提醒,堪称赛博监工。
项目已开源,欢迎star!
https://github.com/zlmica/3d-desktop-pet,「懒得自己运行程序也可以直接到视频介绍下方获取安装包体验,支持多平台」
分享几个关键技术点的实现方案:
一、透明窗口与跨进程通信(核心黑科技)
「实现效果」:宠物始终置顶显示,右键菜单唤起任务/提醒窗口
function createWindow() { const primaryDisplay = screen.getPrimaryDisplay() const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize win = new BrowserWindow({ icon: path.join(process.env.VITE_PUBLIC, 'rabbit.png'), webPreferences: { preload: path.join(__dirname, 'preload.mjs'), }, width: 180, height: 200, x: screenWidth - 200, y: screenHeight - 150, autoHideMenuBar: true, transparent: true, frame: false, alwaysOnTop: true, resizable: false, hasShadow: false, }) // Test active push message to Renderer-process. win.webContents.on('did-finish-load', () => { win?.webContents.send('main-process-message', new Date().toLocaleString()) }) if (VITE_DEV_SERVER_URL) { win.loadURL(VITE_DEV_SERVER_URL) } else { win.loadFile(path.join(RENDERER_DIST, 'index.html')) } win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true, })
关键代码解读:
transparent: true
+frame: false
实现无边框透明窗口alwaysOnTop: true
实现窗口置顶IPC通信控制子窗口状态
「避坑指南」:透明窗口的点击穿透问题通过计算鼠标位置+动态调整窗口大小解决
二、3D宠物交互系统(TresJS魔法)
「模型加载与动画控制」:
const isPlaying = ref(false)const play = async () => { currentAction2.value.play() isPlaying.value = true}const sleep = () => { currentAction2.value.stop() isPlaying.value = false}const hello = () => { if (isPlaying.value) { currentAction2.value.stop() } currentAction.value.reset() currentAction.value.play() currentAction.value.setLoop(THREE.LoopOnce, 1) currentAction.value.clampWhenFinished = true currentAction.value.getMixer().addEventListener('finished', () => { currentAction.value.stop() if (isPlaying.value) { currentAction2.value.play() } })}
使用GLTFLoader加载骨骼动画模型
AnimationMixer实现打招呼/运动/休息状态切换
点击事件穿透处理:将3D对象坐标转换为屏幕坐标
「性能优化」:
空闲状态自动降帧(30FPS → 15FPS)
离屏时暂停渲染
三、任务管理系统(Dexie.js实战)
「数据库设计」:
class PetAppDB extends Dexie { tasks!: Table<Task> reminders!: Table<Reminder> constructor() { super('petApp') // 定义数据库表结构和索引 this.version(1).stores({ tasks: '++id, title, status, priority, createdAt, updatedAt, dueDate', reminders: '++id, title, reminderTime, repeatType, isEnabled, lastAcknowledgedAt, createdAt', }) } /** * 添加任务 * @param {Omit<Task, 'id' | 'createdAt' | 'updatedAt'>} task - 任务数据 * @returns {Promise<string>} 返回新增任务的ID */ async addTask(task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>) { const timestamp = Date.now() return await this.tasks.add({ ...task, createdAt: timestamp, updatedAt: timestamp, }) } /** * 更新任务 * @param {string} id - 任务ID * @param {Partial<Omit<Task, 'id' | 'createdAt'>>} changes - 更新的字段 * @returns {Promise<number>} 返回更新的记录数 */ async updateTask( id: string, changes: Partial<Omit<Task, 'id' | 'createdAt'>> ) { return await this.tasks.update(id, { ...changes, updatedAt: Date.now(), }) } /* 提醒相关方法 ...*/
智能排序算法:未完成 > 有截止时间 > 创建时间
dueDate
字段+定时器实现离线存储:IndexedDB + 本地备份机制
「典型查询示例」:
// 获取24小时内到期的任务const upcomingTasks = await db.tasks .where('dueDate') .between(Date.now(), Date.now() + 86400000) .toArray()
四、提醒系统(多窗口协同)
「架构设计」:
export function useReminder() { /** * 检查是否有需要提醒的事项 */ async function checkReminders() { const reminders = await db.checkReminders() reminderQueue.value = reminders if (reminderQueue.value.length > 0) { window.ipcRenderer.send('show-reminder-window') } }
主窗口:轮询检查
提醒窗口:队列管理+动画效果
IPC消息流:show-reminder-window / hide-reminder-window
「动效实现」:
<TransitionGroup name="reminder"> <div v-for="reminder in reminderQueue" :key="reminder.id" class="relative bg-white rounded-2xl p-4 w-[180px] border border-gray-100 animate-slide-in before:content-[''] before:absolute before:bottom-[-8px] before:left-[30px] before:w-4 before:h-4 before:bg-white before:transform before:rotate-45" > <div class="flex justify-between items-start mb-2"> <h3 class="font-medium select-none">{{ reminder.title }}</h3> <span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded-full ml-2" > {{ new Date(reminder.reminderTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', }) }} </span> </div> <p class="text-gray-600 text-sm mb-4 select-none"> {{ reminder.description || '​' }} </p> <button @click="acknowledgeReminder(reminder)" class="w-[60px] bg-blue-500 text-white py-1.5 rounded-full text-sm hover:bg-blue-600 transition-colors select-none" > 知道了 </button> </div> </TransitionGroup>
使用Vue TransitionGroup + CSS Keyframe实现卡片弹出效果
五、性能优化实践
「窗口管理」:子窗口单例模式(避免内存泄漏)
「3D渲染」:离屏Canvas + 按需渲染
「数据库」:批量操作+索引优化
「打包优化」:electron-builder配置多平台构建
// @see - https://www.electron.build/configuration/configuration{ $schema: "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json", appId: "YourAppID", asar: true, productName: "小桌宠", directories: { output: "release/${version}", }, files: ["dist", "dist-electron"], mac: { target: ["dmg"], artifactName: "${productName}-Mac-${version}-Installer.${ext}", }, win: { target: [ { target: "nsis", arch: ["x64"], }, ], artifactName: "${productName}-Windows-${version}-Setup.${ext}", }, nsis: { oneClick: false, perMachine: false, allowToChangeInstallationDirectory: true, deleteAppDataOnUninstall: false, }, linux: { target: ["AppImage"], artifactName: "${productName}-Linux-${version}.${ext}", },}
结语:桌面应用的思考
通过这个项目验证了Electron在现代桌面开发中的可行性。尽管安装包体积较大,但开发效率和跨平台能力优势明显。未来计划加入:
宠物更换系统 ✅
更多交互功能
宠物更换功能已发布,文章地址:
https://juejin.cn/post/7475544117415624739「顺便一提」:项目结合AI开发,省时又省力,一个字,爽!!!
关注更多AI编程资讯请去AI Coding专区:https://juejin.cn/aicoding
点击"阅读原文"了解详情~