mcpField
P
pixijs-ticker

pixijs-ticker

github.com/pixijs开发3585.9k 次安装4 周前更新
代码仓库

关于 pixijs-ticker

面向 PixiJS 开发者的技能,介绍如何使用 Ticker 驱动更新与渲染循环。内容涵盖回调优先级、生命周期控制,以及不同时间值的用途。

主要功能

  1. 01Ticker 回调与时间值
  2. 02回调优先级排序
  3. 03帧率限制与速度缩放
  4. 04Ticker 和应用生命周期控制
  5. 05显示对象的 onRender 逻辑

适用场景

  1. 01以帧率无关方式制作对象动画
  2. 02限制帧率或控制过大的时间增量
  3. 03暂停和恢复应用渲染循环
  4. 04为显示对象添加逐帧逻辑

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pixijs/pixijs-skills --skill pixijs-ticker

安装后在 Claude Code 中输入/pixijs-ticker确认已加载。

详细介绍

app.ticker 每帧运行已注册的回调,并以 UPDATE_PRIORITY.LOW 驱动 app.render()。每个回调都会接收 Ticker 实例;读取 deltaTime 可获取与帧率无关的乘数(在 60fps 时约为 1.0),读取 deltaMS 可用于实时计算。

快速入门

app.ticker.add((ticker) => {
  sprite.rotation += 0.01 * ticker.deltaTime;
  sprite.x += (200 / 1000) * ticker.deltaMS;
});

app.ticker.add(
  (ticker) => {
    updatePhysics(ticker.deltaMS);
  },
  undefined,
  UPDATE_PRIORITY.HIGH,
);

app.ticker.maxFPS = 30;
app.ticker.speed = 0.5;

sprite.onRender = () => {
  sprite.scale.x = Math.sin(performance.now() / 500);
};

相关技能: pixijs-application(Application 设置和 sharedTicker 选项)、pixijs-performance(帧率优化)、pixijs-migration-v8(v7 Ticker 签名变更)。

核心模式

时间单位

Ticker 提供三个时间值,分别适用于不同场景:

属性类型受 speed 缩放?受 minFPS 限制?使用场景
deltaTime无量纲(在 60fps 时约为 1.0)是是与帧率无关的动画乘数
deltaMS毫秒是是基于时间的计算(像素/秒)
elapsedMS毫秒否否原始测量、性能分析
import { Application } from "pixi.js";

const app = new Application();
await app.init({ width: 800, height: 600 });

app.ticker.add((ticker) => {
  // deltaTime: dimensionless scalar, ~1.0 at 60fps
  sprite.rotation += 0.1 * ticker.deltaTime;

  // deltaMS: real milliseconds (speed-scaled, capped)
  sprite.x += (200 / 1000) * ticker.deltaMS; // 200 pixels per second

  // elapsedMS: raw milliseconds (no scaling, no cap)
  console.log(`Raw frame time: ${ticker.elapsedMS}ms`);
});

易混淆说明: deltaTime 不是毫秒。它等于 deltaMS * Ticker.targetFPMS,其中 targetFPMS 为 0.06(即 1/16.67)。恰好处于 60fps 时,deltaTime 为 1.0;处于 30fps 时,deltaTime 为 2.0。许多人会误把它当作时间值。

优先级顺序和上下文绑定
import { Application, UPDATE_PRIORITY } from "pixi.js";

const app = new Application();
await app.init({ width: 800, height: 600 });

// INTERACTION (50) > HIGH (25) > NORMAL (0) > LOW (-25) > UTILITY (-50)
// app.render() is registered at LOW by the TickerPlugin

app.ticker.add(
  (ticker) => {
    // Physics runs before normal-priority callbacks
    updatePhysics(ticker.deltaMS);
  },
  undefined,
  UPDATE_PRIORITY.HIGH,
);

app.ticker.add((ticker) => {
  // Default priority (NORMAL = 0), runs after HIGH but before render
  updateAnimations(ticker.deltaTime);
});

// Pass `this` as the second argument to preserve context on class methods
class GameSystem {
  public speed = 5;
  public position = 0;

  public update(ticker: Ticker): void {
    this.position += this.speed * ticker.deltaTime;
  }
}

const system = new GameSystem();
app.ticker.add(system.update, system);
app.ticker.remove(system.update, system); // must match both fn and context
帧率上限
import { Ticker } from "pixi.js";

const ticker = new Ticker();

ticker.maxFPS = 30; // Cap at 30fps (skips frames to maintain interval)
ticker.minFPS = 10; // Cap deltaTime so it never exceeds 10fps worth

// If maxFPS < minFPS, minFPS is lowered to match
// If minFPS > maxFPS, maxFPS is raised to match

maxFPS 会跳过更新调用以强制执行帧率上限。minFPS 会限制 deltaTime/deltaMS,避免较大的掉帧产生极大的增量(默认 minFPS 为 10)。

对象级 onRender 钩子
import { Sprite, Assets, Application } from "pixi.js";

const app = new Application();
await app.init({ width: 800, height: 600 });

const texture = await Assets.load("bunny.png");
const sprite = new Sprite(texture);
app.stage.addChild(sprite);

sprite.onRender = (renderer) => {
  sprite.rotation += 0.01;
};

onRender 会在场景图遍历期间、GPU 渲染之前调用。当逻辑与特定显示对象相关时,可以用它替代全局 Ticker 回调。

Ticker.shared、Ticker.system 和 new Ticker
import { Ticker, UPDATE_PRIORITY } from "pixi.js";

// Ticker.shared: singleton, autoStart=true, protected from destroy()
const shared = Ticker.shared;

// Ticker.system: separate instance used by engine background tasks,
// independent of the main scene ticker. It's a plain Ticker instance
// (autoStart=true, _protected=true) with no intrinsic priority; listeners
// are typically added at UTILITY priority by convention.
const system = Ticker.system;

// new Ticker(): custom instance, autoStart=false, you manage the lifecycle
const custom = new Ticker();
custom.autoStart = true; // start when first listener is added
custom.add((ticker) => {
  console.log(ticker.deltaMS);
});

// One-shot callback that auto-removes after firing
custom.addOnce(() => console.log("fires once"), null, UPDATE_PRIORITY.NORMAL);

// When done:
custom.stop();
custom.destroy();

默认情况下,Application 会创建自己的 Ticker。在 app.init() 中设置 sharedTicker: true,即可改用 Ticker.shared。Ticker.shared 和 Ticker.system 都受 _protected 保护,即使调用它们的 destroy(),也不会真正销毁它们。读取 ticker.FPS 可获取测得的帧率,读取 ticker.count 可获取当前监听器数量。

应用生命周期和手动渲染
import { Application } from "pixi.js";

const app = new Application();
await app.init({ autoStart: false });

// Pause and resume the built-in render loop at any time.
app.start();
app.stop();

// Or drive the loop yourself (headless, visibility-gated, fixed timestep, etc.)
function animate() {
  app.ticker.update(); // fires registered callbacks
  app.render(); // renders the stage
  requestAnimationFrame(animate);
}
animate();

app.start() 和 app.stop() 由 TickerPlugin 添加,分别映射到 ticker.start() / ticker.stop()。当需要在切换浏览器标签页时暂停、运行固定时间步长循环或进行离屏渲染时,可使用 autoStart: false,并自行驱动帧循环。

速度缩放
import { Application } from "pixi.js";

const app = new Application();
await app.init({ width: 800, height: 600 });

app.ticker.speed = 0.5; // Half speed (slow motion)
app.ticker.speed = 2.0; // Double speed

// speed affects deltaTime and deltaMS, but NOT elapsedMS

常见错误

[CRITICAL] Ticker 回调会将 delta 作为第一个参数

错误:

app.ticker.add((dt) => {
  bunny.rotation += dt;
});

正确:

app.ticker.add((ticker) => {
  bunny.rotation += ticker.deltaTime;
});

v8 传递给回调的参数是 Ticker 实例,而不是 delta 数值。v7 的写法 (dt) => ... 虽然可以编译,但 dt 实际上是整个 Ticker 对象,因此对它进行算术运算会得到 NaN。

[HIGH] 使用 updateTransform 执行逐帧逻辑

错误:

class MySprite extends Sprite {
  updateTransform() {
    super.updateTransform();
    this.rotation += 0.01;
  }
}

正确:

class MySprite extends Sprite {
  constructor() {
    super();
    this.onRender = this._onRender.bind(this);
  }
  private _onRender() {
    this.rotation += 0.01;
  }
}

v8 已移除 updateTransform。逐对象执行逐帧逻辑时,请使用 onRender 回调。

[MEDIUM] 将 deltaTime 当作毫秒

错误:

app.ticker.add((ticker) => {
  // Tries to move 100px/sec but deltaTime is ~1.0, not ~16.67
  sprite.x += (100 * ticker.deltaTime) / 1000;
});

正确:

app.ticker.add((ticker) => {
  // Using deltaMS for time-based movement
  sprite.x += (100 / 1000) * ticker.deltaMS;
  // Or using deltaTime as a frame-rate multiplier
  sprite.x += 1.5 * ticker.deltaTime;
});

deltaTime 是无量纲标量(在 60fps 时约为 1.0),不是毫秒。实时计算请使用 deltaMS。如果希望实现“以 60fps 运行时每帧执行一次”的效果,请将 deltaTime 用作简单的乘数。

API 参考

评论

支持 Markdown