electron - 无边框、最大化、最小化、等比缩放问题

运行环境 electron@13.1.1,请尽量用最新的修订版本或最新的大版本,否则会有 bug!!!

一. 无边框模式

1. 使用

官方文档:无边框窗口

要创建无边框窗口,只需在 BrowserWindowoptions 中将 frame 设置为 false,如需隐藏mac左上角的红绿灯再添加 titleBarStyle: 'customButtonsOnHover' 参数

const win = new BrowserWindow({ width: 800, height: 600, frame: false, titleBarStyle: 'customButtonsOnHover' })

2. 拖拽区

在无边框模式下,如果没有一拖拽控制区的话,你的应用启动后就一直在屏幕那个位置,无法被移动了,按照用户一般的习惯,应当设置顶部的一块区域为可拖拽的。

我们指定窗口顶部 height: 30px; width: 100%; 的一长条 div 为拖拽区

.drag-part {
  position: absolute;
  z-index: 999;
  top: 0px;
  width: 100%;
  /* 高度自行调整 */
  height: 30px;
  /* 拖拽 */
  -webkit-app-region: drag;
}

😋Tips:你可以右键、或者双击该区域

3. 排除不可拖拽元素

在无边框模式下,我们需要手动添加一些按钮在右上角或者左上角,来控制窗口最大化、最小化、关闭等操作(见下文),如果这些按钮或者 icon 在拖拽区,你是点不动它们的 😂,必须设置其为不可拖拽才能触发点击事件。

.drag-part img {
  -webkit-app-region: no-drag;
}

image-20210617100652275

4. 禁用文本选择

在无框窗口中, 拖动行为可能与选择文本冲突。 例如, 当您拖动标题栏时, 您可能会意外地选择标题栏上的文本。 为防止此操作, 您需要在可区域中禁用文本选择, 如下所选:

.titlebar {
  -webkit-user-select: none;
  -webkit-app-region: drag;
}

二、最大化、最小化、退出操作

上面提到了如何进入无边框模式,当你初次进入 Frameless 模式后,就必然会遇到一个问题,你如何进行常见的窗口最大化、最小化、关闭等操作呢?

网上有很多相关的教程,但是我在使用中还是遇到些问题,在这里想跟大家共同探讨一下。

提前说明一点:最大化不一定是全屏,在Windows上程序最大化仍然可以看到底部导航栏,真正的全屏是看不到的,但mac上最大化就是全屏(🚥左上角绿灯)。

1. 基本用法

以下代码为了使两平台表现一致,我将最大化都处理成了全屏,当然你也可以判断 process.platform === 'darwin' 对平台单独进行处理,稍后会作说明。

渲染进程中点击 button 发出请求 -> 主进程监听并作出响应

main/index.js(主进程 IPC 监听)

import { app, ipcMain } from 'electron'

// 退出程序
ipcMain.on('window-close', function () {
  app.quit()
})
// 最小化
ipcMain.on('window-minimize', function () {
  mainWindow.minimize()
})
// 全屏
ipcMain.on('window-maximize', function () {
  mainWindow.setFullScreen(true)
})
// 退出全屏
ipcMain.on('window-unmaximize', function () {
  mainWindow.setFullScreen(false)
})

在渲染进程中,这里我添加了这几个图标:最小化、最大化(全屏)、退出全屏、关闭

image-20210618142351294

渲染进程代码如下:(用的 Antd-Vue 组件库,加了鼠标悬浮文字提示)

<template>
  <div>
    <a-tooltip placement="bottom" title="最小化">
      <a-icon type="minus" @click="minimize" />
    </a-tooltip>

    <a-tooltip v-show="!fullscreen" placement="bottom" title="全屏">
      <a-icon type="fullscreen" @click="maximize" />
    </a-tooltip>
    <!-- 需要切换显示图标 -->
    <a-tooltip v-show="fullscreen" placement="bottom" title="退出全屏">
      <a-icon type="fullscreen-exit" @click="unmaximize" />
    </a-tooltip>

    <a-tooltip placement="bottom" title="关闭">
      <a-icon type="close" @click="close" />
    </a-tooltip>
  </div>
</template>

<script>
const { ipcRenderer } = require('electron')

export default {
  data() {
    return {
      fullscreen: false
    }
  },
  methods: {
    maximize() {
      this.fullscreen = true
      ipcRenderer.send('window-maximize')
    },
    unmaximize() {
      this.fullscreen = false
      ipcRenderer.send('window-unmaximize')
    },
    minimize() {
      ipcRenderer.send('window-minimize')
    },
    close() {
      ipcRenderer.send('window-close')
    }
  }
}
</script>

2. 说明

  • 主进程中涉及窗口操作的所有 API 文档见 ➡ 链接

  • 最大化(全屏):首先,使用 win.maximize()最大化方法只能在 Windows 上使用,且不能隐藏底部导航栏,为了使多平台表现一致,上面使用了全屏控制方法setFullScreen

  • 如果你不想在Window上使用 最大化==全屏 效果,就只能加判断了:

    ipcMain.on("contrlWindow", (event, args) => {
        switch (args) {
            case 'minimize': win.minimize();break;
            case 'restore':
                if (process.platform === 'darwin') {
                    if (win.isFullScreen()) {
                        win.setFullScreen(false);
                    }
                }
                win.unmaximize();
                break;
            case 'maximize':
                if (process.platform === 'darwin') {
                    win.setFullScreen(true);
                } else {
                    win.maximize();
                }
                break;
            case 'close': win && win.close();break;
            case 'openDev': win.webContents.openDevTools();break;
            case 'backHome':
                if (process.platform === 'darwin') {
                    win.setFullScreen(false);
                }
                win.setSize(1048, 700);win.setResizable(false);win.center();break;
            default: break;
        }
        event.returnValue = 'setSuccess'
    });
    
  • 退出全屏win.restore()window.unmaximize() 方法在 macOS 上不起作用,所以使用win.setFullScreen(false)

  • 退出win.close() 方法也可以关闭窗口,但在 macOS 上程序仍然会驻留在 Dock 上,如果有完全退出的需求,请使用app.quit() 方法

  • remote 实现方式:链接


三. 窗口等比缩放

有时候我们要显示的内容是包含高清图片或者时要做一个视频播放器,为了要适配不同分辨率的屏幕,或者为了保证用户体验,用户在调整窗口尺寸后,需要保证原图片或者视频能按等比例缩放。

有两种实现方式:

方式一:监听 will-resize 事件

Electron 中的 BrowserWindow 在每次重新缩放大小前会触发will-resize事件,我们只要拦截这个事件,并手动设置窗口大小为想要的大小。

简单看下实现过程就好了。。。

const WIDTH = 1440
const HEIGHT = 900
const aspectRatio = WIDTH / HEIGHT // 窗口宽高比

const mainWindow = new BrowserWindow({
  width: WIDTH,
  height: HEIGHT,
  frame: false, // 无边框窗口
  show: false
})

mainWindow.loadFile('src/index.html')
mainWindow.once('ready-to-show', () => {
  // 限制窗口最小尺寸(int整形), 无边框模式下,不考虑标题栏高度
  mainWindow.setMinimumSize(WIDTH / 2, HEIGHT / 2)
  mainWindow.show()
})

// 控制等比缩放
mainWindow.on('will-resize', resizeWindow)
function resizeWindow(event, newBounds) {
  const win = event.sender
  event.preventDefault() // 拦截,使窗口先不变
  const currentSize = win.getSize()
  const widthChanged = currentSize[0] !== newBounds.width // 判断是宽变了还是高变了,两者都变优先按宽适配
  // ! 虽然搞不懂为何有1px偏差,但是可以解决问题(Windows 10)
  if (widthChanged) {
    win.setContentSize(newBounds.width - 1, parseInt(newBounds.width / aspectRatio + 0.5) - 1)
  } else {
    win.setContentSize(parseInt(aspectRatio * newBounds.height + 0.5) - 1, newBounds.height - 1)
  }
}

👍 方式二:setAspectRatio 方法

之前遇到问题总是想着去问百度,殊不知官方文档才是最好的教程,之所以饶了上面这么复杂的一个大弯,是因为官方 API 文档。。。。。。。太 T M 多了 😭😭,根本没注意到还有这么一个简单的方法 🙃:

win.setAspectRatio(aspectRatio[, extraSize])

比如设置一个 16:10 的窗口比例:

mainWindow.setAspectRatio(1.6)

就这么简单,macOS 上如有问题请移步: - 详细文档


相关参考:

  • 10
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值