stage.js

'use strict'

// ******************************** 舞台对象 ********************************

const Stage = new class {
  resolution = {
    width: 0,
    height: 0,
  }

  /** 初始化舞台 */
  async initialize() {
    // 加载配置和全局数据文件
    await Promise.all([
      Data.loadConfig(),
      Data.loadGlobalData(),
    ])

    // 设置网页标题
    const title = document.createElement('title')
    title.textContent = Data.config.window.title
    document.head.appendChild(title)

    // 设置body样式
    document.body.style.margin = '0'
    document.body.style.overflow = 'hidden'

    // 设置初始分辨率
    this.resolution.width = Data.globalData.canvasWidth
    this.resolution.height = Data.globalData.canvasHeight

    // 调整大小
    this.resize()

    // 侦听事件
    window.on('resize', this.resize)
  }

  /**
   * 设置分辨率
   * @param {number} width 分辨率宽度
   * @param {number} height 分辨率高度
   * @param {number} sceneScale 场景缩放系数
   * @param {number} uiScale 界面缩放系数
   */
  setResolution(width, height, sceneScale, uiScale) {
    this.resolution.width = width
    this.resolution.height = height
    this.resize()
    Mouse.resize()
    Scene.setScale(sceneScale)
    UI.setScale(uiScale)
  }

  /**
   * 缩放画布
   * @param {Object} resolution 分辨率数据对象
   */
  scaleCanvas(resolution) {
    const canvasWidth = resolution.width
    const canvasHeight = resolution.height
    const parentWidth = window.innerWidth
    const parentHeight = window.innerHeight
    const {container} = GL
    // 禁止旋转容器元素
    container.style.transform = ''
    if (canvasWidth / canvasHeight >= parentWidth / parentHeight) {
      // 如果画布宽高比大于容器宽高比,则上下留黑边
      const scaledHeight = Math.round(canvasHeight / canvasWidth * parentWidth)
      container.style.left = '0'
      container.style.top = `${parentHeight - scaledHeight >> 1}px`
      container.style.width = `${parentWidth}px`
      container.style.height = `${scaledHeight}px`
    } else {
      // 如果画布宽高比小于容器宽高比,则左右留黑边
      const scaledWidth = Math.round(canvasWidth / canvasHeight * parentHeight)
      container.style.left = `${parentWidth - scaledWidth >> 1}px`
      container.style.top = '0'
      container.style.width = `${scaledWidth}px`
      container.style.height = `${parentHeight}px`
    }
  }

  /**
   * 旋转并缩放画布
   * @param {Object} resolution 分辨率数据对象
   */
  rotateAndScaleCanvas(resolution) {
    const canvasWidth = resolution.width
    const canvasHeight = resolution.height
    const parentWidth = window.innerHeight
    const parentHeight = window.innerWidth
    const {container} = GL
    // 以左上角为锚点,旋转容器元素90度
    container.style.transformOrigin = 'left top'
    container.style.transform = 'rotate(90deg)'
    if (canvasWidth / canvasHeight >= parentWidth / parentHeight) {
      // 如果画布宽高比大于容器宽高比,则上下留黑边
      const scaledHeight = Math.round(canvasHeight / canvasWidth * parentWidth)
      container.style.left = `${parentHeight + scaledHeight >> 1}px`
      container.style.top = '0'
      container.style.width = `${parentWidth}px`
      container.style.height = `${scaledHeight}px`
    } else {
      // 如果画布宽高比小于容器宽高比,则左右留黑边
      const scaledWidth = Math.round(canvasWidth / canvasHeight * parentHeight)
      container.style.left = `${parentHeight}px`
      container.style.top = `${parentWidth - scaledWidth >> 1}px`
      container.style.width = `${scaledWidth}px`
      container.style.height = `${parentHeight}px`
    }
  }

  /** 重新调整大小事件 */
  resize() {
    const resolution = Stage.resolution
    GL.resize(resolution.width, resolution.height)
    switch (Stats.deviceType) {
      case 'pc':
        // 个人电脑模式
        Stage.scaleCanvas(resolution)
        break
      case 'mobile':
        // 移动设备模式:如果最小分辨率宽高比和窗口宽高比同时>=1或同时<1,则不用旋转
        if ((resolution.width >= resolution.height) === (window.innerWidth >= window.innerHeight)) {
          // 不旋转
          Stage.scaleCanvas(resolution)
        } else {
          // 旋转90度
          Stage.rotateAndScaleCanvas(resolution)
        }
        break
    }
  }
}