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

相关推荐
su1ka1113 分钟前
re题(35)BUUCTF-[FlareOn4]IgniteMe
前端
测试界柠檬4 分钟前
面试真题 | web自动化关闭浏览器,quit()和close()的区别
前端·自动化测试·软件测试·功能测试·程序人生·面试·自动化
多多*5 分钟前
OJ在线评测系统 登录页面开发 前端后端联调实现全栈开发
linux·服务器·前端·ubuntu·docker·前端框架
2301_801074155 分钟前
TypeScript异常处理
前端·javascript·typescript
小阿飞_7 分钟前
报错合计-1
前端
caperxi8 分钟前
前端开发中的防抖与节流
前端·javascript·html
霸气小男8 分钟前
react + antDesign封装图片预览组件(支持多张图片)
前端·react.js
susu10830189119 分钟前
前端css样式覆盖
前端·css
学习路上的小刘11 分钟前
vue h5 蓝牙连接 webBluetooth API
前端·javascript·vue.js
&白帝&11 分钟前
vue3常用的组件间通信
前端·javascript·vue.js