electron开发自定义的titlebar

之前一直使用的是 custom-electron-titlebar 这个库,但是因为electron版本不兼容,以及自定义的关系,决定还是自己动手写一个吧

取消electron自带的titlebar

js 复制代码
const win = new BrowserWindow({
  width: 800,
  height: 600,
  frame: false, // 取消自带的titlebar
  webPreferences: {
    preload,
    webSecurity: false,
    nodeIntegration: true,
    contextIsolation: false,
  },
})

在preload/index.ts里实现自定义titlebar

我们同样使用vue、jsx实现,因为titlebar比较简单,就不需要引入其他库了

为什么要在preload里实现

其实在应用代码里也可以实现,如果你的应用只会加载自己的页面的话没什么影响,但是如果通过修改href跳转到其他网站,就会出现没有顶部控制条的情况。 preload是每个窗口都会加载的,保证窗口一定有控制条。

使用vite编译preload/index.ts

因为我们是tsx写的,所以需要编译

js 复制代码
// scripts/build-preload.js
const path = require('path')
const { build } = require('vite')
const { builtinModules } = require('module')
const jsx = require('@vitejs/plugin-vue-jsx')

async function buildPreload(watch) {
  const child = await build({
    configFile: false, // 不继承根目录vite.config配置
    publicDir: false, // 编译结果不需要拷贝public文件夹
    root: path.join(__dirname, '..'),
    base: './',
    plugins: [jsx()], // 支持jsx
    build: {
      outDir: './dist_preload',
      assetsDir: '', // 这个要格外小心,使用默认的 assets 会导致在 Electron 打包后基于 file:// 加载文件失败
      minify: false,
      rollupOptions: {
        // 入口
        input: {
          preload: path.resolve(__dirname, '../preload/index.ts'),
        },

        // electron 目前只支持 CommonJs 格式
        output: {
          format: 'cjs',
          entryFileNames: '[name].js',
        },

        // 不打包内置的包,例如 fs、path...
        external: [
          'electron',
          '@electron/remote',
          ...builtinModules,
        ],
      },

      // 一般传 {},每次preload有变化都会重新编译
      watch,
    },
    optimizeDeps: {
      exclude: ['electron'], // 告诉 Vite 排除预构建 electron,不然会出现 __diranme is not defined
    },
  })

  return child
}

实现preload/titlebar.tsx

为了方便大家理解,我删掉了大部分的业务代码,保留了主题结构,基本就是开发一个vue组件,再结合一点electron窗口api

js 复制代码
import { onMounted, ref, defineComponent } from 'vue'
import { getCurrentWindow, nativeImage } from '@electron/remote'

const win = getCurrentWindow()

export default defineComponent({
  props: ['options'],

  setup(props) {
    const { options } = props

    const state = ref()

    // logo
    const logo = nativeImage.createFromPath(options.icon as string).toDataURL()

    // 菜单
    const menuList = DefaultMenu

    onMounted(() => {
      updateMaximized()

      win.on('maximize', updateMaximized)
      win.on('unmaximize', updateMaximized)
    })

    function updateMaximized() {}

    function onMinimize() {}

    function onMaximize() {}

    function onClose() {}

    function onClickMenu(e: Event, item: MenuItem) {}

    function onMouseOverMenu(item: MenuItem) {}

    function onClickMenuItem(e: Event, item: any) {}

    return () => {
      const { ifShowDropdown, hoverListId } = state.value

      return (
        <>
          <div class="logo">
            <img src={logo} width="20" height="20" />
          </div>

          <div class="menu"></div>

          <div class="title"></div>

          <div class="controls"></div>
        </>
      )
    }
  },
})

给titlebar填加样式

样式文件需要在preload一加载的时候就添加到dom里

  • 新建样式文件prelaod/style.css

  • 在preload/index.ts中引入

js 复制代码
// vite 会把带raw标志的css文件处理成字符串
import defaultStyle from './style.css?raw'

function styled(strs: string) {
  const $style = document.createElement('style')
  $style.innerHTML = strs
  document.head.appendChild($style)
}

在preload/index.ts里初始化titlebar

js 复制代码
import { createApp } from 'vue'
import Titlebar from './titlebar'

/**
 * dom loaded后才能操作dom
 */
window.addEventListener('DOMContentLoaded', () => {
  initTitlebar()
})

/**
 * 初始化titlebar, 返回 titlebar组件实例
 */
function initTitlebar(cusOptions: Options = {}) {
  const options = {
    disableTitle: false,
    ...cusOptions,
  }

  // init style
  styled(defaultStyle)

  // 创建titlebar dom节点
  const $titlebar = document.createElement('div')
  document.body.insertBefore($titlebar, document.body.firstChild)

  // 将Titlebar组件插入到dom里, 并返回组件实例
  return createApp(Titlebar, { options }).mount($titlebar)
}

从外部控制titlebar

有的时候,我们还需要从应用里面控制titlebar的状态,这个时候是和操作vue组件实例是一样的

  • 组件暴露方法
js 复制代码
setup(props, { expose }) {
  const state = ref()

  expose({
    changeState() {
      ....
    }
  })
}
  • 通过titlebar实例调用
js 复制代码
const $titlebar = initTitlebar()

$titlebar.changeState()

到这里,我们基本实现了titlebar,剩下的就是调整我们原来app的样式,要给顶部的titlebar留出一定的高度。

相关推荐
开开心心就好1 小时前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
Setsuna_F_Seiei3 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲5 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙5 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan7 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
kyriewen7 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理8 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
+VX:Fegn08958 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
雪芽蓝域zzs8 小时前
第 7 章:双 Y 轴组合图(柱状 + 折线,看板高频场景)
vue.js·echars
IT_陈寒8 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端