app.ticker exécute les callbacks enregistrés chaque frame et pilote app.render() à UPDATE_PRIORITY.LOW. Chaque callback reçoit l'instance Ticker ; lisez deltaTime comme un multiplicateur indépendant de la fréquence d'images (≈1.0 à 60fps) ou deltaMS pour les calculs en temps réel.
Démarrage rapide
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);
};
Skills associées : pixijs-application (configuration de l'Application et option sharedTicker), pixijs-performance (optimisation du taux de rafraîchissement), pixijs-migration-v8 (changements de signature du ticker en v7).
Modèles principaux
Unités de temps
Le Ticker expose trois valeurs de temps, chacune pour des cas d'usage différents :
| Propriété | Type | Affectée par speed ? | Limitée par minFPS ? | Cas d'usage |
|---|---|---|---|---|
deltaTime |
sans dimension (~1.0 à 60fps) | oui | oui | Multiplicateurs d'animation indépendants de la fréquence d'images |
deltaMS |
millisecondes | oui | oui | Calculs basés sur le temps (pixels/sec) |
elapsedMS |
millisecondes | non | non | Mesure brute, profiling |
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.add((ticker) => {
// deltaTime: scalaire sans dimension, ~1.0 à 60fps
sprite.rotation += 0.1 * ticker.deltaTime;
// deltaMS: millisecondes réelles (affectée par speed, limitée)
sprite.x += (200 / 1000) * ticker.deltaMS; // 200 pixels par seconde
// elapsedMS: millisecondes brutes (sans scaling, sans limite)
console.log(`Raw frame time: ${ticker.elapsedMS}ms`);
});
Note importante : deltaTime n'est pas des millisecondes. C'est deltaMS * Ticker.targetFPMS où targetFPMS est 0.06 (soit 1/16.67). À exactement 60fps, deltaTime est 1.0. À 30fps, deltaTime est 2.0. C'est un point qui surprend les développeurs qui le traitent comme une valeur de temps.
Ordonnancement des priorités et liaison de contexte
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() est enregistré à LOW par le TickerPlugin
app.ticker.add(
(ticker) => {
// La physique s'exécute avant les callbacks de priorité normale
updatePhysics(ticker.deltaMS);
},
undefined,
UPDATE_PRIORITY.HIGH,
);
app.ticker.add((ticker) => {
// Priorité par défaut (NORMAL = 0), s'exécute après HIGH mais avant render
updateAnimations(ticker.deltaTime);
});
// Passez `this` comme deuxième argument pour préserver le contexte sur les méthodes de classe
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); // doit correspondre à fn et context
Limitation du taux de rafraîchissement
import { Ticker } from "pixi.js";
const ticker = new Ticker();
ticker.maxFPS = 30; // Limiter à 30fps (ignore les frames pour maintenir l'intervalle)
ticker.minFPS = 10; // Limiter deltaTime pour qu'il ne dépasse jamais 10fps worth
// Si maxFPS < minFPS, minFPS est abaissé pour correspondre
// Si minFPS > maxFPS, maxFPS est augmenté pour correspondre
maxFPS ignore les appels de mise à jour pour appliquer un plafond. minFPS limite deltaTime/deltaMS pour que les grandes chutes de frame ne produisent pas de deltas énormes (minFPS par défaut est 10).
Hook onRender par objet
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 est appelé lors du parcours du graphe de scène, avant le rendu GPU. C'est une alternative à un callback ticker global quand la logique est liée à un objet d'affichage spécifique.
Ticker.shared, Ticker.system, et new Ticker
import { Ticker, UPDATE_PRIORITY } from "pixi.js";
// Ticker.shared: singleton, autoStart=true, protégé contre destroy()
const shared = Ticker.shared;
// Ticker.system: instance séparée utilisée par les tâches de fond du moteur,
// indépendante du ticker de la scène principale. C'est une instance Ticker ordinaire
// (autoStart=true, _protected=true) sans priorité intrinsèque ; les listeners
// sont généralement ajoutés à priorité UTILITY par convention.
const system = Ticker.system;
// new Ticker(): instance personnalisée, autoStart=false, vous gérez le cycle de vie
const custom = new Ticker();
custom.autoStart = true; // démarrer quand le premier listener est ajouté
custom.add((ticker) => {
console.log(ticker.deltaMS);
});
// Callback unique qui se supprime automatiquement après son exécution
custom.addOnce(() => console.log("fires once"), null, UPDATE_PRIORITY.NORMAL);
// Quand c'est fini :
custom.stop();
custom.destroy();
Application crée son propre Ticker par défaut. Définissez sharedTicker: true dans app.init() pour utiliser Ticker.shared à la place. Ticker.shared et Ticker.system sont tous deux _protected et ne seront pas réellement détruits si vous appelez destroy() sur eux. Lisez ticker.FPS pour le taux de rafraîchissement mesuré et ticker.count pour le nombre actuel de listeners.
Cycle de vie de l'app et rendu manuel
import { Application } from "pixi.js";
const app = new Application();
await app.init({ autoStart: false });
// Pausez et reprenez la boucle de rendu intégrée à tout moment.
app.start();
app.stop();
// Ou pilotez la boucle vous-même (headless, gated par visibility, timestep fixe, etc.)
function animate() {
app.ticker.update(); // exécute les callbacks enregistrés
app.render(); // rend la scène
requestAnimationFrame(animate);
}
animate();
app.start() et app.stop() sont ajoutés par le TickerPlugin et mappent à ticker.start() / ticker.stop(). Utilisez autoStart: false plus votre propre driver de frame quand vous avez besoin de pause sur blur d'onglet, d'exécuter une boucle avec timestep fixe, ou de rendu hors écran.
Mise à l'échelle de la vitesse
import { Application } from "pixi.js";
const app = new Application();
await app.init({ width: 800, height: 600 });
app.ticker.speed = 0.5; // Demi-vitesse (slow motion)
app.ticker.speed = 2.0; // Vitesse double
// speed affecte deltaTime et deltaMS, mais PAS elapsedMS
Erreurs courantes
[CRITIQUE] Le callback Ticker attend delta comme premier argument
Incorrect :
app.ticker.add((dt) => {
bunny.rotation += dt;
});
Correct :
app.ticker.add((ticker) => {
bunny.rotation += ticker.deltaTime;
});
v8 passe l'instance Ticker comme argument du callback, pas un nombre de delta. Le pattern v7 (dt) => ... compile mais dt est l'objet Ticker entier, donc les opérations arithmétiques sur lui produisent NaN.
[ÉLEVÉ] Utiliser updateTransform pour la logique par frame
Incorrect :
class MySprite extends Sprite {
updateTransform() {
super.updateTransform();
this.rotation += 0.01;
}
}
Correct :
class MySprite extends Sprite {
constructor() {
super();
this.onRender = this._onRender.bind(this);
}
private _onRender() {
this.rotation += 0.01;
}
}
updateTransform a été supprimé en v8. Utilisez le callback onRender pour la logique par frame et par objet.
[MOYEN] Traiter deltaTime comme des millisecondes
Incorrect :
app.ticker.add((ticker) => {
// Tente de déplacer 100px/sec mais deltaTime est ~1.0, pas ~16.67
sprite.x += (100 * ticker.deltaTime) / 1000;
});
Correct :
app.ticker.add((ticker) => {
// Utiliser deltaMS pour le mouvement basé sur le temps
sprite.x += (100 / 1000) * ticker.deltaMS;
// Ou utiliser deltaTime comme multiplicateur de fréquence d'images
sprite.x += 1.5 * ticker.deltaTime;
});
deltaTime est un scalaire sans dimension (~1.0 à 60fps), pas des millisecondes. Utilisez deltaMS pour les calculs en temps réel. Utilisez deltaTime comme un simple multiplicateur quand vous voulez un comportement « par frame à 60fps ».