Skip to content

UI适配不同分辨率

随着越来越多的设备采用高分辨率显示屏(如2K、4K等),在开发过程中遇到的UI适配问题也日益增多。通常,我们会将文本缩放调整为150%或更高,以提高显示效果。然而,这样在本地开发时调整好的UI,放到其他电脑上后,可能会出现显示异常,比如按钮过小、文字过小、图片不合适等,这通常是由分辨率和缩放比例不同引起的。

自适应设计的重要性

如果在页面开发初期即采用自适应设计,便可有效避免因不同分辨率和缩放倍率导致的UI显示问题。然而,如果页面已经开发完成且发现不适配各类分辨率,重新编写代码则会带来较大的工作量。因此,我们可以考虑通过计算调整,使UI在不同分辨率和缩放倍率下保持一致的显示效果。

实现不同分辨率适配的方案

在Electron中,可以通过以下步骤实现页面自适应:

获取屏幕分辨率与缩放比例:使用Electron的screen模块获取当前屏幕的分辨率宽度。假设我们选择一个基准分辨率值(例如1920px),将当前分辨率宽度除以基准值即可得到一个缩放比例。

  1. 设置缩放比例:将计算得到的缩放比例赋值给窗口的zoomFactor属性,这样可以确保内容在不同分辨率下的大小保持一致。

  2. 动态调整窗口大小:基于缩放比例调整窗口的宽度和高度,以确保UI布局的协调性。

  3. 通过这种方式,可以使得UI在不同设备上都能保持相对一致的体验效果,避免重写代码带来的额外工作量。

示例代码

写一个工具类,用来设置窗口的缩放比例和窗口大小,代码如下:

这个工具类是在主进程中使用的。

typescript
import { screen, BrowserWindow } from 'electron'

interface WindowSizeOption {
  /**
   * 是否居中
   */
  center?: boolean
  /**
   * 窗口大小
   */
  size?: {
    width: number
    height: number
    isZoom: boolean
  }
}

export default class WindowZoomUtil {

  /**
   * 基准宽度
   */
  private baseWidth: number = 1920

  /**
   * 获取当前缩放比例
   */
  static getZoom(): number {
    const display = screen.getPrimaryDisplay()
    const { width } = display.size
    return width / baseWidth
  }

  /**
   * 监听窗口事件,这些事件可能会重置`zoomFactor`的值,所以需要重新设置一遍
   */
  listenerEventOnResetZoom(win: BrowserWindow): void {
    win.on('focus', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('show', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('maximize', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('unmaximize', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('restore', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('enter-full-screen', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('leave-full-screen', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.on('kiosk', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
    win.webContents.on('did-finish-load', () => {
      WindowZoomUtil.setWindowFactory(win)
    })
  }

  /**
   * 设置窗口缩放比例
   * @param win 要设置的窗口
   */
  static setWindowFactory(win: BrowserWindow): void {
    const zoom = WindowZoomUtil.getZoom()
    win?.webContents.setZoomFactor(zoom)
  }

  /**
   * 设置窗口尺寸
   * @param win     要设置的窗口
   * @param options 属性
   */
  static setWindowSize(win: BrowserWindow, options?: WindowSizeOption): void {
    const zoom = WindowZoomUtil.getZoom()

    // 设置窗口尺寸
    if (options?.size) {
      const [width, height] = win.getSize()
      win.setSize(Math.round(width * zoom), Math.round(height * zoom))
    } else {
      const { width, height, isZoom } = options.size
      const w = isZoom ? Math.round(width * zoom) : width
      const h = isZoom ? Math.round(height * zoom) : height
      win.setSize(w, h)
    }

    // 设置窗口位置
    if (options?.center) {
      win.center()
    }
  }
}

在窗口创建时,调用这个工具类,示例代码:

typescript
import { app, BrowserWindow, screen } from 'electron'
import WindowZoomUtil from './WindowZoomUtil'

function createWindow() {
  const win = new BrowserWindow({
    // ... 配置项省略
  })

  // 监听系统缩放倍率的变化
  screen.on('display-metrics-changed', (_, display) => {
    if (display.id === screen.getPrimaryDisplay().id) {
      WindowZoomUtil.setWindowFactory(win)
      WindowZoomUtil.setWindowSize(win, { center: true })
    }
  })

  WindowZoomUtil.listenerEventOnResetZoom(win)

  win.on('ready-to-show', () => {
    win.show()
    WindowZoomUtil.setWindowSize(win, { center: true })
  })
}

app.whenReady().then(() => {
  createWindow()
})