Published on

PixiJS 源码揭秘 - 2. 深入探究Application模块

Authors
  • avatar
    Name
    青雲
    Twitter

引言

PixiJS 是一个高性能的 2D 渲染引擎,广泛应用于游戏开发和交互式应用程序的构建。它提供了一系列强大的功能,使开发者能够轻松创建复杂的图形和动画效果。PixiJS 的设计旨在充分利用现代浏览器的图形处理能力,支持 WebGL 和 Canvas 渲染,使得渲染效果流畅且高效。

在 PixiJS 中,Application 模块的作用至关重要。它不仅简化了应用程序的构建流程,还整合了渲染器、场景图和Ticker系统,使得开发和管理复杂的 2D 应用变得更加轻松。

具体来说,Application 模块提供以下几项核心功能:

  • 渲染器管理:自动检测并初始化最佳渲染器(如 WebGL 或 Canvas),确保在各种设备上的高效运行。
  • 场景图管理:创建一个根容器(stage),用于管理和组织所有的显示对象。
  • 事件和动画管理:通过集成 Ticker 系统,提供了一个稳定的更新循环,简化了动画和事件驱动的开发。

Application 模块不仅是构建 PixiJS 应用程序的起点,也是后续开发和优化的重要基础。它封装了常用的初始化流程,使得开发者无需关心底层的实现细节,只需集中精力在业务逻辑和视觉效果的构建上。

Application类的设计与实现

PixiJS中的Application类是构建应用程序的核心,它将渲染器、场景图和Ticker系统整合到一个便捷的接口中,极大地简化了图形应用的开发流程。本章将详细分析Application类的结构、构造函数和初始化过程。

Application 类的结构

Application 类主要包含以下几个关键属性和方法:

  1. 属性
    • stage: 根显示容器(Container),是场景图的起点,用于管理和组织所有的显示对象。
    • renderer: 渲染器实例,负责将 stage 中的内容绘制到画布上。
    • _plugins: 静态属性,存储已安装插件的集合,用于扩展应用程序的功能。
  2. 方法
    • constructor(): 构造函数,用于创建 Application 实例。
    • init(options): 初始化方法,设置应用程序的选项并创建渲染器。
    • render(): 渲染当前场景。
    • destroy(rendererDestroyOptions, options): 销毁应用程序及其资源,释放内存。
    • canvasview: 获取渲染器的画布元素。
    • screen: 获取渲染器的屏幕矩形。

构造函数

Application类的构造函数支持多种签名,可以不带参数,也可以带有配置选项:

  1. 无参数构造
    • constructor()
    • 直接创建一个默认的Application实例。
  2. 带参数构造
    • constructor(options?: Partial<ApplicationOptions>)
    • 可接受一个配置对象,其中包含初始化选项如autoStartresizeTosharedTicker等属性。

需要注意的是,从版本 8.0.0 开始,构造函数中传入选项的方式被标记为过时,更推荐使用 init 方法来进行初始化。这一策略的原因在于,init 方法提供了更清晰的初始化流程,有助于分离构造和初始化的逻辑,从而提高代码的可维护性和可读性。

constructor(...args: [Partial<ApplicationOptions>] | [])
{
  if (args[0] !== undefined)
  {
    deprecation(v8_0_0, 'Application constructor options are deprecated, please use Application.init() instead.');
  }
}

生命周期管理

PixiJS 的 Application 类管理了应用程序的整个生命周期,包括启动、渲染和销毁。

流程图

Application启动

Application 类的启动过程由init()方法控制,该方法用于初始化渲染器、场景图和其他必要组件。默认情况下,autoStart选项为true,这意味着在初始化之后会自动启动 Ticker。

  1. init方法
    • init(options?: Partial<ApplicationOptions>)
    • 该方法用于初始化应用程序,接收一个可选的配置对象。
  2. 选项处理
    • init 方法接收一个可选的参数对象,该对象用于覆盖默认配置。通过合并用户提供的选项和默认选项,确保应用程序能够根据需要进行灵活配置。
  3. 自动检测渲染器
    • 使用autoDetectRenderer函数自动检测并初始化最佳渲染器(WebGL或Canvas)。
    • 这一过程考虑了设备和浏览器的兼容性,确保在各种平台上的最佳性能。
public async init(options?: Partial<ApplicationOptions>)
{
  options = { ...options };
  this.renderer = await autoDetectRenderer(options as ApplicationOptions) as R;

  // 安装插件
  Application._plugins.forEach((plugin) =>
    {
      plugin.init.call(this, options);
    });
}
  1. 启动Ticker(如果autoStart为true)
    • 如果初始化选项中的autoStart为true,则自动启动Ticker,开始渲染循环。
// TickerPlugin
public static init(options?: PixiMixins.ApplicationOptions): void
  {
    // Set default
    options = Object.assign({
      autoStart: true,
      sharedTicker: false,
    }, options);


  // Start the rendering
  if (options.autoStart) {
    this.start();
  }	
}

自定义Ticker回调

即使设置了autoStart,也可以通过自定义回调函数来控制Ticker的行为。例如:

// 假设我们已经构建了应用程序实例并初始化了应用
const app = new Application();
await app.init({ background: '#1099bb', resizeTo: window, autoStart: true });
document.body.appendChild(app.canvas);

// 创建一个精灵
const bunny = new Sprite('path/to/bunny.png');
bunny.anchor.set(0.5);
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
app.stage.addChild(bunny);

// 添加自定义的Ticker回调函数
let elapsed = 0.0;
app.ticker.add((delta) => {
    elapsed += delta;
    bunny.x = 100.0 + Math.cos(elapsed / 50.0) * 100.0;
});

示例代码

以下是启动Application的示例代码:

import { Application, Sprite } from 'pixi.js';

// 创建应用程序实例
const app = new Application();

// 初始化应用程序
await app.init({ background: '#1099bb', resizeTo: window, autoStart: true });

// 获取渲染器的画布并将其添加到DOM
document.body.appendChild(app.canvas);

插件机制

Application类的一个强大特性是支持插件机制。在初始化阶段,可以通过插件系统来扩展功能,使应用程序更加灵活和强大。

  1. 插件安装
    • 插件可以通过静态方法extensions.add()注册,添加到_plugins数组中。
    • extensions.handleByList()用于批量处理插件列表,把插件批量添加到应用程序中。
    • 在初始化时(init方法中),会遍历并调用每个插件的init方法。
  2. 插件的实现示例
    • ResizePluginTickerPlugin 为例,它们在应用初始化阶段自动添加相应的功能,使得应用程序能够自动调整画布大小和支持基于时间的更新循环。
// 固定安装的插件,如 ResizePlugin 和 TickerPlugin。
extensions.add(ResizePlugin);
extensions.add(TickerPlugin);

// 批量处理插件列表
extensions.handleByList(ExtensionType.Application, Application._plugins);
  1. 卸载插件
    • 在销毁应用程序时,同样也需要处理插件的卸载。插件的卸载会按照与注册相反的顺序进行,以确保依赖关系正确处理。

Application销毁

当不再需要应用程序时,可以调用destroy()方法来释放所有资源,避免内存泄漏。销毁过程包括:

  1. 插件销毁:
    • 按注册的逆序遍历并调用每个插件的destroy()方法。
  2. 销毁场景图:
    • 销毁根容器(stage)及其所有子元素。
  3. 销毁渲染器:
    • 销毁渲染器及其相关资源。
public destroy(rendererDestroyOptions: RendererDestroyOptions = { removeView: false }, options: DestroyOptions = {}): void
{
    // 复制插件数组,并反转它们的顺序
    const plugins = Application._plugins.slice(0).reverse();
    plugins.forEach((plugin) => {
        plugin.destroy.call(this);
    });

    // 销毁场景图
    this.stage.destroy(options);
    this.stage = null;

    // 销毁渲染器
    this.renderer.destroy(rendererDestroyOptions);
    this.renderer = null;
}

示例代码

以下示例说明如何销毁Application实例:

// 停止渲染循环并销毁应用程序
app.ticker.stop();
app.destroy({ removeView: true }, { children: true, texture: true, textureSource: true, context: true });

生命周期图

通过这些生命周期管理方法,开发者可以确保PixiJS应用程序在使用期间高效运行,并在不需要时妥善释放所有资源,保持系统的良好性能。

总结

本文中,我们详细探讨了 PixiJS 中Application类的设计与实现,并分析了其在构建高性能2D渲染应用中的关键作用。通过对Application结构、启动流程、插件机制和销毁过程的深入解析,帮助开发者更好地理解和使用PixiJS来实现复杂的图形和动画效果。