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留出一定的高度。

相关推荐
懒大王爱吃狼32 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風5 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫5 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦6 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子6 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山7 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享7 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
程序媛小果7 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
从兄8 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
凉辰8 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式