从0到1打造会催你搬砖的3D桌面宠物:Electron + Vue3实战心得

1,975 阅读4分钟

当二次元遇上生产力工具

最近开发了一款名为"小桌宠"的跨平台桌面应用,将3D宠物与任务管理结合。这只兔子不仅能卖萌,还会在你摸鱼时弹出提醒,堪称赛博监工。

image.png

项目已开源:地址,欢迎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 || '&#8203;' }}
        </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实现卡片弹出效果


五、性能优化实践

  1. 窗口管理:子窗口单例模式(避免内存泄漏)
  2. 3D渲染:离屏Canvas + 按需渲染
  3. 数据库:批量操作+索引优化
  4. 打包优化: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开发,省时又省力,一个字,爽!!!