平台适配

Layout 最开始设计出来是为了提升微信小游戏开放数据域开发效率,但现在 Layout 已经被用到各种各样的小游戏平台,作为轻量级 Canvas 应用的解决方案。

对于渲染引擎,一定会依赖平台的能力,比如创建图片、监听事件等,一般引擎的解决方案是主动适配主要运行平台,比如适配浏览器和微信小游戏。Layout 同样如此,目前已经适配了 浏览器、微信小游戏、字节小游戏和百度小游戏平台,但如果一个全新的平台想要使用 Layout,无需更改 Layout 源码,同样可以进行适配,下面进行简单的介绍。

原理

Layout 对于平台环境的依赖并不多,主要是事件监听、图片创建等,所有平台相关的依赖都被封装到 env 模块,它非常精简,代码如下:

import { Callback } from "./types";

if (typeof GameGlobal !== 'undefined') {
  GameGlobal.__env = GameGlobal.wx || GameGlobal.tt || GameGlobal.swan;
}

const domEventMap: Record<string, string> = {
  touchstart: 'mousedown',
  touchmove: 'mousemove',
  touchend: 'mouseup',
  touchcancel: 'mouseleave',
}

enum eventType {
  on = 'on',
  off = 'off',
}

function genDomTouchEvent(event: string, type: eventType) {
  if (typeof document !== 'undefined') {
    return function (listener: Callback) {
      type === eventType.on ?
        document.addEventListener(event, listener, false)
        : document.removeEventListener(event, listener, false)
    }
  }
}

function getOnTouchHandler(event: string, type: eventType) {
  if (typeof GameGlobal !== 'undefined') {
    return GameGlobal.__env[`${type}${event}`]
  } else {
    return genDomTouchEvent(domEventMap[event.toLowerCase()], type);
  }
}

/**
 * Layout 可能用在不用的平台,而Layout会依赖平台下面的一些方法来实现具体的功能,比如创建图片
 * 为了更好做平台适配,统一封装 env 模块,不同平台要做适配,替换 env 的具体方法即可
 */
export default {
  // 监听触摸相关事件
  onTouchStart: getOnTouchHandler('TouchStart', eventType.on),
  onTouchMove: getOnTouchHandler('TouchMove', eventType.on),
  onTouchEnd: getOnTouchHandler('TouchEnd', eventType.on),
  onTouchCancel: getOnTouchHandler('TouchCancel', eventType.on),
  // 取消监听触摸相关事件
  offTouchStart: getOnTouchHandler('TouchStart', eventType.off),
  offTouchMove: getOnTouchHandler('TouchMove', eventType.off),
  offTouchEnd: getOnTouchHandler('TouchEnd', eventType.off),
  offTouchCancel: getOnTouchHandler('TouchCancel', eventType.off),

  // Layout 支持百分比样式,如果根节点样式设置为 100%,直接取 Canvas 的尺寸,不同平台的取法不一样,因此单独提供函数
  getRootCanvasSize() {
    if (typeof __env !== 'undefined' && __env.getSharedCanvas) {
      const cvs = __env.getSharedCanvas();
      return {
        width: cvs.width,
        height: cvs.height,
      }
    } else {
      return {
        width: 300,
        height: 150,
      }
    }
  },

  // 取当前设备的 devicePixelRatio,不同平台的取法不一样
  getDevicePixelRatio() {
    if (typeof __env !== 'undefined' && __env.getSystemInfoSync) {
      return __env.getSystemInfoSync().devicePixelRatio;
    } else if (window.devicePixelRatio) {
      return window.devicePixelRatio;
    } else {
      return 1;
    }
  },

  // 创建Canvas
  createCanvas() {
    if (typeof __env !== 'undefined') {
      return __env.createCanvas();
    }

    return document.createElement('canvas');
  },

  // 创建图片
  createImage() {
    if (typeof __env !== 'undefined') {
      return __env.createImage();
    }
    return document.createElement('img');
  }
}

示例

因此想要实现平台适配,只需要在 Layout 初始化之前魔改 env 模块的实现即可,下面是一个例子:

import { env, Layout } from 'minigame-canvas-engine';

env.createImage = () => {
  return new ImageClassInCurrentPlatform();
}

env.onTouchStart = currentPlatform.onTouchStart;

// 下面正常执行 Layout的使用