PixiJS expose des primitives mathématiques légères (Point, Matrix, classes de formes) utilisées dans la bibliothèque pour les transformations, les tests de collision et la conversion de coordonnées. Importez pixi.js/math-extras pour ajouter des opérations vectorielles (add, dot, magnitude, reflect) et des helpers d'intersection/union de Rectangle.
Démarrage rapide
const parent = new Container();
parent.position.set(100, 100);
parent.scale.set(2);
app.stage.addChild(parent);
const child = new Container();
child.position.set(50, 50);
parent.addChild(child);
const globalPt = child.toGlobal(new Point(0, 0));
const m = new Matrix()
.translate(100, 50)
.rotate(Math.PI / 4)
.scale(2, 2);
const world = m.apply(new Point(10, 20));
const hitArea = new Rectangle(0, 0, 200, 100);
console.log(hitArea.contains(50, 50));
Skills liées : pixijs-scene-container (propriétés de transformation), pixijs-events (utilisation de hitArea), pixijs-scene-core-concepts (culling avec Rectangle).
Modèles principaux
Point et ObservablePoint
Point est un simple type {x, y}. ObservablePoint déclenche un callback quand x ou y change ; il est utilisé en interne par les propriétés position, scale, pivot, origin et skew d'un Container.
import { Point } from "pixi.js";
const p = new Point(10, 20);
p.set(30, 40); // définir les deux
p.set(50); // x=50, y=50
const clone = p.clone();
console.log(p.equals(clone)); // true
p.copyFrom({ x: 1, y: 2 }); // accepte tout PointData
// Point.shared : point temporaire, réinitialisé à (0,0) à chaque accès
const temp = Point.shared;
temp.set(100, 200);
// ne pas conserver de référence à Point.shared
Les propriétés d'un Container comme position, scale, pivot, origin et skew sont des ObservablePoints. Définir .x ou .y sur eux déclenche automatiquement le recalcul de la transformation.
import { Container } from "pixi.js";
const obj = new Container();
obj.position.set(100, 200); // déclenche l'observateur -> marque la transformation comme dirty
obj.position.x = 150; // déclenche aussi l'observateur
Matrix (transformation affine 2D)
Matrix représente une transformation affine 3x3 : | a c tx | b d ty | 0 0 1 |. Elle supporte translate, scale, rotate, append, prepend, invert et decompose.
import { Matrix, Point } from "pixi.js";
// Construire une transformation
const m = new Matrix()
.translate(100, 50)
.rotate(Math.PI / 4)
.scale(2, 2);
// Transformer un point (espace local -> espace parent)
const local = new Point(10, 20);
const world = m.apply(local);
// Transformer inversement (espace parent -> espace local)
const backToLocal = m.applyInverse(world);
// Combiner des matrices
const a = new Matrix().translate(50, 0);
const b = new Matrix().rotate(Math.PI / 2);
a.append(b); // a = a * b
// Décomposer en position/scale/rotation/skew
const transform = {
position: new Point(),
scale: new Point(),
pivot: new Point(),
skew: new Point(),
rotation: 0,
};
m.decompose(transform);
console.log(transform.rotation); // ~0.785 (PI/4)
// Matrice temporaire partagée (réinitialisée à chaque accès)
const temp = Matrix.shared;
// IDENTITY est une référence en lecture seule
const isDefault = m.equals(Matrix.IDENTITY);
Transformations de coordonnées via Container
Les Containers fournissent toGlobal, toLocal et getGlobalPosition pour la conversion de coordonnées.
import { Container, Point } from "pixi.js";
const parent = new Container();
parent.position.set(100, 100);
parent.scale.set(2);
const child = new Container();
child.position.set(50, 50);
parent.addChild(child);
// Point local dans l'espace du child -> espace global (monde)
const globalPt = child.toGlobal(new Point(0, 0));
// globalPt = { x: 200, y: 200 } (100 + 50*2, 100 + 50*2)
// Point global -> espace local du child
const localPt = child.toLocal(new Point(200, 200));
// localPt = { x: 0, y: 0 }
// Convertir entre deux containers
const other = new Container();
other.position.set(300, 300);
const ptInOther = child.toLocal(new Point(10, 10), other);
Formes et tests de collision
Rectangle, Circle, Ellipse, Polygon, RoundedRectangle et Triangle implémentent tous contains(x, y) pour les tests point-in-shape, plus getBounds(out?) et strokeContains(x, y, width, alignment?). Ils peuvent être utilisés comme hitArea sur des containers pour les régions d'interaction personnalisées.
import { Rectangle, Circle, Polygon, Container } from "pixi.js";
const rect = new Rectangle(0, 0, 200, 100);
rect.contains(50, 50); // true
rect.contains(300, 50); // false
rect.left; // 0
rect.right; // 200
rect.top; // 0
rect.bottom; // 100
rect.isEmpty(); // false (Rectangle.EMPTY retourne un nouveau rectangle vide)
// Méthodes natives Rectangle-to-Rectangle (pas besoin de math-extras)
const other = new Rectangle(50, 50, 100, 100);
rect.containsRect(other); // true si `other` est complètement à l'intérieur de `rect`
rect.intersects(other); // booléen : se chevauchent-ils ?
rect.intersects(other, matrix); // chevauchement après transformation de `other`
// Test de collision sur le contour (alignment: 1 = intérieur, 0.5 = centré, 0 = extérieur)
rect.strokeContains(0, 50, 4); // true si (0,50) se trouve sur un contour de 4px centré
const circle = new Circle(100, 100, 50);
circle.strokeContains(150, 100, 4, 1); // vérification du contour aligné intérieurement
// getBounds fonctionne sur chaque forme (retourne un Rectangle, accepte un paramètre out)
const bounds = circle.getBounds();
const reused = new Rectangle();
new Polygon([0, 0, 100, 0, 50, 100]).getBounds(reused);
// Utiliser comme zone d'interaction
const button = new Container();
button.hitArea = new Rectangle(0, 0, 200, 50);
button.eventMode = "static";
button.on("pointerdown", () => {
/* cliqué */
});
Ne confondez pas la méthode native Rectangle.intersects(other) (retourne boolean) avec intersection(other) de math-extras (retourne un Rectangle décrivant la zone de chevauchement).
Helpers de disposition Rectangle
Rectangle est fourni avec des helpers mutants utilisés massivement en UI/layout, agrégation de limites et pixel snapping. Tous retournent this pour le chaînage.
import { Rectangle } from "pixi.js";
const r = new Rectangle(10, 10, 100, 50);
r.pad(5); // agrandir de tous les côtés : x=5, y=5, w=110, h=60
r.pad(10, 4); // padding horizontal/vertical séparé
r.scale(2); // multiplier x, y, width, height par 2
// fit rétrécit `this` pour qu'il se trouve à l'intérieur d'un autre rectangle (clipping)
const viewport = new Rectangle(0, 0, 200, 200);
new Rectangle(150, 150, 200, 200).fit(viewport); // -> 150, 150, 50, 50
// enlarge agrandit `this` pour inclure un autre rectangle (agrégation de limites)
const total = new Rectangle();
items.forEach((item) =>
total.enlarge(new Rectangle().copyFromBounds(item.getBounds())),
);
// ceil accroche à une grille de pixels (resolution: 1 = pixels entiers, 2 = demi-pixels)
new Rectangle(10.2, 10.6, 100.8, 100.4).ceil();
// copier directement les limites d'un Container/Mesh dans un Rectangle
new Rectangle().copyFromBounds(container.getBounds());
Polygon
Polygon accepte quatre formats de constructeur : un tableau de nombres plat, un tableau d'objets de type point, ou l'un ou l'autre passé en arguments spread.
import { Polygon, Point } from "pixi.js";
new Polygon([0, 0, 100, 0, 50, 100]); // nombres plats
new Polygon([new Point(0, 0), new Point(100, 0), new Point(50, 100)]); // PointData[]
new Polygon(0, 0, 100, 0, 50, 100); // nombres spread
new Polygon(new Point(0, 0), new Point(100, 0), new Point(50, 100)); // points spread
const poly = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]);
poly.points; // [0, 0, 100, 0, 100, 100, 0, 100] (tableau plat mutable)
poly.closePath; // true par défaut ; false produit un chemin ouvert
poly.startX; // 0 - premier vertex
poly.lastX; // 0 - dernier vertex (lastY pour y)
poly.isClockwise(); // test d'enroulement shoelace (utile pour la détection de trous SVG)
// Contenance polygon-in-polygon pour la détection de trous
const outer = new Polygon([0, 0, 100, 0, 100, 100, 0, 100]);
const hole = new Polygon([25, 25, 75, 25, 75, 75, 25, 75]);
outer.containsPolygon(hole); // true
Constantes
import { DEG_TO_RAD, RAD_TO_DEG, PI_2 } from "pixi.js";
const angle = 45 * DEG_TO_RAD; // 0.785...
const degrees = angle * RAD_TO_DEG; // 45
const fullCircle = PI_2; // Math.PI * 2
Types
PointData- interface minimale{x, y}acceptée par la plupart des APIs. Utilisez-la quand vous typez des paramètres qui n'ont besoin que de lire les coordonnées.PointLike- étendPointDataavecset(),copyFrom(),copyTo(),equals(). Implémenté par Point et ObservablePoint.Size- interface{ width, height }utilisée par les APIs renderer/canvas.SHAPE_PRIMITIVE- string literal union :'rectangle' | 'circle' | 'ellipse' | 'polygon' | 'roundedRectangle' | 'triangle'. Chaque forme exposetypepour que vous puissiez vous brancher sansinstanceof.
import type { PointData } from "pixi.js";
function distance(a: PointData, b: PointData): number {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
math-extras (import side-effect)
import 'pixi.js/math-extras' ajoute des méthodes à Point, ObservablePoint et Rectangle via extension de prototype. Non inclus dans le bundle par défaut.
import "pixi.js/math-extras";
import { Point } from "pixi.js";
Méthodes vectorielles Point / ObservablePoint
Toutes les méthodes acceptent un paramètre out optionnel pour éviter les allocations. Sans out, un nouveau Point est retourné.
const a = new Point(3, 4);
const b = new Point(1, 2);
// Arithmétique
const sum = a.add(b); // Point(4, 6)
const diff = a.subtract(b); // Point(2, 2)
const prod = a.multiply(b); // Point(3, 8) - composante par composante
const scaled = a.multiplyScalar(2); // Point(6, 8)
// Produit scalaire et produit vectoriel
const dot = a.dot(b); // 11
const cross = a.cross(b); // 2 (composante z du produit vectoriel 3D)
// Longueur
const len = a.magnitude(); // 5
const lenSq = a.magnitudeSquared(); // 25 (plus rapide pour les comparaisons)
// Normaliser en vecteur unitaire
const unit = a.normalize(); // Point(0.6, 0.8)
// Projection et réflexion
const proj = a.project(b); // projeter a sur b
const refl = a.reflect(new Point(0, 1)); // réfléchir sur la normale
// Rotation
const rotated = a.rotate(Math.PI / 2); // tourner de 90 degrés
// Réutiliser un point existant pour éviter une allocation
const out = new Point();
a.add(b, out); // résultat écrit dans out
Méthodes étendues de Rectangle
containsRect et intersects sont des méthodes natives de Rectangle (voir ci-dessus). math-extras ajoute equals, intersection (retourne le rectangle de chevauchement) et union :
import "pixi.js/math-extras";
import { Rectangle } from "pixi.js";
const r1 = new Rectangle(0, 0, 100, 100);
const r2 = new Rectangle(50, 50, 100, 100);
r1.equals(r2); // false
const overlap = r1.intersection(r2); // Rectangle(50, 50, 50, 50)
const envelope = r1.union(r2); // Rectangle(0, 0, 150, 150)
// Paramètre out optionnel
const out = new Rectangle();
r1.intersection(r2, out);
Fonctions utilitaires de géométrie
Ces fonctions sont exportées depuis pixi.js/math-extras, pas depuis l'entrée principale pixi.js.
import {
floatEqual,
lineIntersection,
segmentIntersection,
} from "pixi.js/math-extras";
import { Point } from "pixi.js";
// Comparaison de floats basée sur epsilon (epsilon par défaut : Number.EPSILON)
floatEqual(0.1 + 0.2, 0.3, 1e-10); // true avec un epsilon raisonnable
floatEqual(1.0, 1.001, 0.01); // true (epsilon personnalisé)
// Intersection de lignes non bornées (retourne {x: NaN, y: NaN} si parallèles)
const hit = lineIntersection(
new Point(0, 0),
new Point(10, 10), // ligne A
new Point(10, 0),
new Point(0, 10), // ligne B
); // Point(5, 5)
if (isNaN(hit.x)) {
/* les lignes sont parallèles */
}
// Intersection de segments bornés (retourne {x: NaN, y: NaN} si les segments ne se croisent pas)
const segHit = segmentIntersection(
new Point(0, 0),
new Point(10, 10),
new Point(10, 0),
new Point(0, 10),
); // Point(5, 5)
if (isNaN(segHit.x)) {
/* les segments ne se croisent pas */
}
Erreurs courantes
HIGH : Importer depuis @pixi/math
Incorrect :
import { Point } from "@pixi/math";
Correct :
import { Point } from "pixi.js";
v8 utilise un seul package pixi.js. Tous les sous-packages comme @pixi/math, @pixi/core, etc. ont été supprimés.
MEDIUM : Muter ObservablePoint sans déclencher l'observateur
Incorrect :
// Remplacer la référence perd l'observation
let pos = container.position;
pos = new Point(100, 200); // container.position inchangé
Correct :
// Muter sur place pour déclencher l'observateur
container.position.set(100, 200);
// ou
container.position.x = 100;
container.position.y = 200;
// ou copier depuis un autre point
container.position.copyFrom(new Point(100, 200));
La position, scale, pivot, origin et skew d'un Container sont des ObservablePoints. Définir .x ou .y sur eux déclenche la mise à jour de la transformation du container. Réassigner la référence de variable ne modifie pas le container. Mutez toujours l'ObservablePoint existant via .set(), .copyFrom() ou une assignation de propriété directe sur l'objet original.
MEDIUM : Ne pas importer math-extras pour les méthodes étendues
Incorrect :
import { Point } from "pixi.js";
const p = new Point(1, 2);
p.add(new Point(3, 4)); // TypeError: p.add is not a function
Correct :
import "pixi.js/math-extras";
import { Point } from "pixi.js";
const p = new Point(1, 2);
const sum = p.add(new Point(3, 4)); // fonctionne
Les utilitaires mathématiques étendus (add, subtract, multiply, magnitude, normalize, dot, cross, etc. sur Point ; méthodes d'intersection sur les formes) requièrent un explicit import 'pixi.js/math-extras'. Ceux-ci ne sont pas inclus dans le bundle par défaut.
MEDIUM : Stocker des références à des objets partagés/temporaires
Incorrect :
const myPoint = Point.shared;
myPoint.set(100, 200);
// ... plus tard ...
console.log(myPoint.x); // 0 (réinitialisé au prochain accès)
Correct :
const myPoint = new Point();
myPoint.copyFrom(Point.shared.set(100, 200));
// ou simplement
const myPoint = new Point(100, 200);
Point.shared et Matrix.shared sont réinitialisés à zéro/identité chaque fois qu'on y accède. Ils existent pour des calculs ponctuels dans une seule expression. Ne stockez jamais une référence à eux.