当二次元遇上生产力工具
最近开发了一款名为"小桌宠"的跨平台桌面应用,将3D宠物与任务管理结合。这只兔子不仅能卖萌,还会在你摸鱼时弹出提醒,堪称赛博监工。
项目已开源:地址,欢迎star
懒得自己运行程序也可以直接到视频介绍下方获取安装包体验,支持多平台
分享几个关键技术点的实现方案:
一、透明窗口与跨进程通信(核心黑科技)
实现效果:宠物始终置顶显示,右键菜单唤起任务/提醒窗口
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在现代桌面开发中的可行性。尽管安装包体积较大,但开发效率和跨平台能力优势明显。未来计划加入:
- 宠物更换系统 ✅
- 更多交互功能
宠物更换功能已发布,文章地址
顺便一提:项目结合AI开发,省时又省力,一个字,爽!!!