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

相关推荐
沉默璇年24 分钟前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder30 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_8827275740 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
会发光的猪。1 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客2 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记2 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安2 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼2 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
周全全2 小时前
Spring Boot + Vue 基于 RSA 的用户身份认证加密机制实现
java·vue.js·spring boot·安全·php
Domain-zhuo2 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式