pixijs-ticker

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
app.ticker
runs registered callbacks every frame and drives
app.render()
at
UPDATE_PRIORITY.LOW
. Each callback receives the Ticker instance; read
deltaTime
as a frame-rate-independent multiplier (≈1.0 at 60fps) or
deltaMS
for real-time calculations.
app.ticker
会逐帧执行已注册的回调函数,并以
UPDATE_PRIORITY.LOW
优先级驱动
app.render()
方法。每个回调函数会接收Ticker实例;
deltaTime
是与帧率无关的乘数(60fps下约为1.0),
deltaMS
则适用于实时计算场景。

Quick Start

快速开始

ts
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);
};
Related skills:
pixijs-application
(Application setup and sharedTicker option),
pixijs-performance
(frame rate optimization),
pixijs-migration-v8
(v7 ticker signature changes).
ts
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
(应用程序设置与sharedTicker选项)、
pixijs-performance
(帧率优化)、
pixijs-migration-v8
(v7版本Ticker签名变更)。

Core Patterns

核心模式

Time units

时间单位

The Ticker exposes three timing values, each for different use cases:
PropertyTypeScaled by speed?Capped by minFPS?Use case
deltaTime
dimensionless (~1.0 at 60fps)yesyesFrame-rate-independent animation multipliers
deltaMS
millisecondsyesyesTime-based calculations (pixels/sec)
elapsedMS
millisecondsnonoRaw measurement, profiling
ts
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`);
});
Tension note:
deltaTime
is not milliseconds. It is
deltaMS * Ticker.targetFPMS
where targetFPMS is 0.06 (i.e. 1/16.67). At exactly 60fps, deltaTime is 1.0. At 30fps, deltaTime is 2.0. This catches people who treat it as a time value.
Ticker提供三种时间值,分别适用于不同场景:
属性名类型是否受speed缩放?是否受minFPS限制?使用场景
deltaTime
无量纲值(60fps下约为1.0)与帧率无关的动画乘数计算
deltaMS
毫秒数基于时间的计算(如像素/秒)
elapsedMS
毫秒数原始时间测量、性能分析
ts
import { Application } from "pixi.js";

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

app.ticker.add((ticker) => {
  // deltaTime:无量纲标量,60fps下约为1.0
  sprite.rotation += 0.1 * ticker.deltaTime;

  // deltaMS:实际毫秒数(受speed缩放、帧率限制影响)
  sprite.x += (200 / 1000) * ticker.deltaMS; // 每秒移动200像素

  // elapsedMS:原始毫秒数(不受缩放、限制影响)
  console.log(`原始帧时间:${ticker.elapsedMS}ms`);
});
注意事项:
deltaTime
不是毫秒数,它等于
deltaMS * Ticker.targetFPMS
,其中targetFPMS为0.06(即1/16.67)。在恰好60fps时,deltaTime为1.0;在30fps时,deltaTime为2.0。很多人会误将其当作时间值使用,这点需要注意。

Priority ordering and context binding

优先级排序与上下文绑定

ts
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
ts
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() 由TickerPlugin注册为LOW优先级

app.ticker.add(
  (ticker) => {
    // 物理更新在普通优先级回调之前执行
    updatePhysics(ticker.deltaMS);
  },
  undefined,
  UPDATE_PRIORITY.HIGH,
);

app.ticker.add((ticker) => {
  // 默认优先级(NORMAL = 0),在HIGH之后、渲染之前执行
  updateAnimations(ticker.deltaTime);
});

// 将`this`作为第二个参数传入,以保留类方法的上下文
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); // 必须同时匹配函数和上下文

Frame rate capping

帧率限制

ts
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
skips update calls to enforce a ceiling.
minFPS
caps deltaTime/deltaMS so large frame drops don't produce enormous deltas (default minFPS is 10).
ts
import { Ticker } from "pixi.js";

const ticker = new Ticker();

ticker.maxFPS = 30; // 限制帧率为30fps(通过跳帧维持间隔)
ticker.minFPS = 10; // 限制deltaTime的最大值,使其不会超过10fps对应的数值

// 如果maxFPS < minFPS,minFPS会被降低至与maxFPS一致
// 如果minFPS > maxFPS,maxFPS会被提高至与minFPS一致
maxFPS
通过跳过更新调用来强制设置帧率上限。
minFPS
用于限制deltaTime/deltaMS的最大值,避免帧率骤降时产生过大的时间增量(默认minFPS为10)。

Per-object onRender hook

逐对象onRender钩子函数

ts
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
is called during scene graph traversal, before GPU rendering. It is an alternative to a global ticker callback when logic is tied to a specific display object.
ts
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, and new Ticker

Ticker.shared、Ticker.system与自定义Ticker实例

ts
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
creates its own Ticker by default. Set
sharedTicker: true
in
app.init()
to use
Ticker.shared
instead.
Ticker.shared
and
Ticker.system
are both
_protected
and will not actually be destroyed if you call
destroy()
on them. Read
ticker.FPS
for the measured frame rate and
ticker.count
for the current listener count.
ts
import { Ticker, UPDATE_PRIORITY } from "pixi.js";

// Ticker.shared:单例实例,autoStart=true,调用destroy()不会被销毁
const shared = Ticker.shared;

// Ticker.system:独立实例,用于引擎后台任务,
// 与主场景Ticker无关。它是一个普通的Ticker实例
// (autoStart=true,_protected=true),本身无固有优先级;按照惯例,监听器通常以UTILITY优先级添加。
const system = Ticker.system;

// new Ticker():自定义实例,autoStart=false,由你管理生命周期
const custom = new Ticker();
custom.autoStart = true; // 当添加第一个监听器时自动启动
custom.add((ticker) => {
  console.log(ticker.deltaMS);
});

// 一次性回调,触发后自动移除
custom.addOnce(() => console.log("仅触发一次"), null, UPDATE_PRIORITY.NORMAL);

// 使用完毕后:
custom.stop();
custom.destroy();
Application
默认会创建自己的Ticker实例。在
app.init()
中设置
sharedTicker: true
即可改用
Ticker.shared
Ticker.shared
Ticker.system
都被标记为
_protected
,调用
destroy()
不会真正销毁它们。可以通过
ticker.FPS
查看当前测量的帧率,通过
ticker.count
查看当前监听器数量。

App lifecycle and manual rendering

应用生命周期与手动渲染

ts
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()
and
app.stop()
are added by the
TickerPlugin
and map to
ticker.start()
/
ticker.stop()
. Use
autoStart: false
plus your own frame driver when you need to pause on tab blur, run a fixed-timestep loop, or render offscreen.
ts
import { Application } from "pixi.js";

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

// 随时暂停和恢复内置渲染循环
app.start();
app.stop();

// 或者自行驱动循环(适用于无头模式、可见性控制、固定时间步长等场景)
function animate() {
  app.ticker.update(); // 触发已注册的回调函数
  app.render(); // 渲染舞台
  requestAnimationFrame(animate);
}
animate();
app.start()
app.stop()
TickerPlugin
提供,对应
ticker.start()
/
ticker.stop()
。当你需要在标签页失焦时暂停、运行固定时间步长循环或离屏渲染时,可以设置
autoStart: false
并使用自定义的帧驱动逻辑。

Speed scaling

速度缩放

ts
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
ts
import { Application } from "pixi.js";

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

app.ticker.speed = 0.5; // 半速(慢动作)
app.ticker.speed = 2.0; // 双倍速

// speed会影响deltaTime和deltaMS,但不会影响elapsedMS

Common Mistakes

常见错误

[CRITICAL] Ticker callback expects delta as first argument

[严重] Ticker回调函数错误地期望delta作为第一个参数

Wrong:
ts
app.ticker.add((dt) => {
  bunny.rotation += dt;
});
Correct:
ts
app.ticker.add((ticker) => {
  bunny.rotation += ticker.deltaTime;
});
v8 passes the Ticker instance as the callback argument, not a delta number. The v7 pattern
(dt) => ...
compiles but
dt
is the entire Ticker object, so arithmetic on it produces
NaN
.
错误写法:
ts
app.ticker.add((dt) => {
  bunny.rotation += dt;
});
正确写法:
ts
app.ticker.add((ticker) => {
  bunny.rotation += ticker.deltaTime;
});
v8版本中,回调函数的参数是Ticker实例,而非单个delta数值。v7版本中使用的
(dt) => ...
写法虽然能编译通过,但
dt
实际是整个Ticker对象,对其进行算术运算会得到
NaN

[HIGH] Using updateTransform for per-frame logic

[高风险] 使用updateTransform处理逐帧逻辑

Wrong:
ts
class MySprite extends Sprite {
  updateTransform() {
    super.updateTransform();
    this.rotation += 0.01;
  }
}
Correct:
ts
class MySprite extends Sprite {
  constructor() {
    super();
    this.onRender = this._onRender.bind(this);
  }
  private _onRender() {
    this.rotation += 0.01;
  }
}
updateTransform
was removed in v8. Use the
onRender
callback for per-object per-frame logic.
错误写法:
ts
class MySprite extends Sprite {
  updateTransform() {
    super.updateTransform();
    this.rotation += 0.01;
  }
}
正确写法:
ts
class MySprite extends Sprite {
  constructor() {
    super();
    this.onRender = this._onRender.bind(this);
  }
  private _onRender() {
    this.rotation += 0.01;
  }
}
v8版本中已移除
updateTransform
方法。对于逐对象的逐帧逻辑,请使用
onRender
回调函数。

[MEDIUM] Treating deltaTime as milliseconds

[中风险] 将deltaTime当作毫秒数使用

Wrong:
ts
app.ticker.add((ticker) => {
  // Tries to move 100px/sec but deltaTime is ~1.0, not ~16.67
  sprite.x += (100 * ticker.deltaTime) / 1000;
});
Correct:
ts
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
is a dimensionless scalar (~1.0 at 60fps), not milliseconds. Use
deltaMS
for real time calculations. Use
deltaTime
as a simple multiplier when you want "per frame at 60fps" behavior.
错误写法:
ts
app.ticker.add((ticker) => {
  // 试图以100px/sec的速度移动,但deltaTime约为1.0,而非16.67
  sprite.x += (100 * ticker.deltaTime) / 1000;
});
正确写法:
ts
app.ticker.add((ticker) => {
  // 使用deltaMS进行基于时间的移动计算
  sprite.x += (100 / 1000) * ticker.deltaMS;
  // 或者将deltaTime作为帧率乘数使用
  sprite.x += 1.5 * ticker.deltaTime;
});
deltaTime
是无量纲标量(60fps下约为1.0),并非毫秒数。实时计算请使用
deltaMS
。当你需要“60fps下逐帧执行”的行为时,可以将
deltaTime
作为简单乘数使用。

API Reference

API参考