pixijs-events

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
PixiJS's federated event system mirrors DOM events on the scene graph. Set
container.eventMode = 'static'
to opt an object in, then listen with
.on()
,
addEventListener()
, or
onEventName
property handlers. Move events fire only over the listening object; use
globalpointermove
for drag.
PixiJS的联合事件系统在场景图上镜像DOM事件。设置
container.eventMode = 'static'
将对象设为可交互,然后通过
.on()
addEventListener()
onEventName
属性处理器监听事件。移动事件仅在监听对象上方触发;如需拖拽功能,请使用
globalpointermove

Quick Start

快速开始

ts
const button = new Sprite(await Assets.load("button.png"));
button.eventMode = "static";
button.cursor = "pointer";
app.stage.addChild(button);

button.on("pointertap", (event) => {
  console.log("clicked at", event.global.x, event.global.y);
});

let dragging = false;
button.on("pointerdown", () => {
  dragging = true;
});
button.on("pointerup", () => {
  dragging = false;
});
button.on("pointerupoutside", () => {
  dragging = false;
});
button.on("globalpointermove", (event) => {
  if (dragging) button.parent.toLocal(event.global, undefined, button.position);
});
Related skills:
pixijs-accessibility
(screen reader + keyboard),
pixijs-scene-dom-container
(HTML overlays),
pixijs-performance
(event-heavy scenes).
ts
const button = new Sprite(await Assets.load("button.png"));
button.eventMode = "static";
button.cursor = "pointer";
app.stage.addChild(button);

button.on("pointertap", (event) => {
  console.log("clicked at", event.global.x, event.global.y);
});

let dragging = false;
button.on("pointerdown", () => {
  dragging = true;
});
button.on("pointerup", () => {
  dragging = false;
});
button.on("pointerupoutside", () => {
  dragging = false;
});
button.on("globalpointermove", (event) => {
  if (dragging) button.parent.toLocal(event.global, undefined, button.position);
});
相关技能:
pixijs-accessibility
(屏幕阅读器+键盘)、
pixijs-scene-dom-container
(HTML叠加层)、
pixijs-performance
(事件密集型场景)。

Core Patterns

核心模式

eventMode values

eventMode取值

ts
import { Sprite } from "pixi.js";

const sprite = new Sprite();

// No interaction at all; children also ignored
sprite.eventMode = "none";

// Default. Self not interactive; interactive children still work
sprite.eventMode = "passive";

// Hit tested only when a parent is interactive
sprite.eventMode = "auto";

// Standard interaction: receives pointer/mouse/touch events
sprite.eventMode = "static";

// Like static, but also fires synthetic events from the ticker
// when the pointer is stationary (for animated objects under cursor)
sprite.eventMode = "dynamic";
Use
'static'
for buttons, UI elements, and drag targets. Use
'dynamic'
only for objects that move under a stationary cursor and need continuous hover updates.
Use
isInteractive()
to check whether an object can receive events:
ts
sprite.eventMode = "static";
sprite.isInteractive(); // true

sprite.eventMode = "passive";
sprite.isInteractive(); // false
ts
import { Sprite } from "pixi.js";

const sprite = new Sprite();

// 完全不交互;其子元素也会被忽略
sprite.eventMode = "none";

// 默认值。自身不可交互;但可交互的子元素仍能正常工作
sprite.eventMode = "passive";

// 仅当父元素可交互时才会进行命中测试
sprite.eventMode = "auto";

// 标准交互:接收指针/鼠标/触摸事件
sprite.eventMode = "static";

// 类似static,但当指针静止时,也会从ticker触发合成事件
// 适用于光标下的动画对象
sprite.eventMode = "dynamic";
'static'
用于按钮、UI元素和拖拽目标。仅当对象在静止光标下移动且需要持续悬停更新时,才使用
'dynamic'
使用
isInteractive()
检查对象是否能接收事件:
ts
sprite.eventMode = "static";
sprite.isInteractive(); // true

sprite.eventMode = "passive";
sprite.isInteractive(); // false

Event types

事件类型

Pointer events (recommended for cross-device compatibility):
pointerdown
,
pointerup
,
pointerupoutside
,
pointermove
,
pointerover
,
pointerout
,
pointerenter
,
pointerleave
,
pointertap
,
pointercancel
.
Mouse events:
mousedown
,
mouseup
,
mouseupoutside
,
mousemove
,
mouseover
,
mouseout
,
mouseenter
,
mouseleave
,
click
,
rightdown
,
rightup
,
rightupoutside
,
rightclick
,
wheel
.
Touch events:
touchstart
,
touchend
,
touchendoutside
,
touchmove
,
touchcancel
,
tap
. Each touch carries
altKey
,
ctrlKey
,
metaKey
, and
shiftKey
copied from the native
TouchEvent
, so modifier keys work the same as with mouse or pointer events.
Global move events:
globalpointermove
,
globalmousemove
,
globaltouchmove
. These fire on every pointer movement regardless of whether the pointer is over the listening object.
Container lifecycle events (no
eventMode
required):
added
,
removed
,
destroyed
,
childAdded
,
childRemoved
,
visibleChanged
.
指针事件(推荐用于跨设备兼容性):
pointerdown
pointerup
pointerupoutside
pointermove
pointerover
pointerout
pointerenter
pointerleave
pointertap
pointercancel
鼠标事件:
mousedown
mouseup
mouseupoutside
mousemove
mouseover
mouseout
mouseenter
mouseleave
click
rightdown
rightup
rightupoutside
rightclick
wheel
触摸事件:
touchstart
touchend
touchendoutside
touchmove
touchcancel
tap
。每个触摸事件都会复制原生
TouchEvent
altKey
ctrlKey
metaKey
shiftKey
,因此修饰键的工作方式与鼠标或指针事件相同。
全局移动事件:
globalpointermove
globalmousemove
globaltouchmove
。无论指针是否在监听对象上方,这些事件都会在每次指针移动时触发。
容器生命周期事件(无需设置
eventMode
):
added
removed
destroyed
childAdded
childRemoved
visibleChanged

Listening styles

监听方式

ts
import { Sprite } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";

// EventEmitter style (recommended)
const handler = (e) => console.log("clicked");
sprite.on("pointerdown", handler);
sprite.once("pointerdown", handler); // one-time
sprite.off("pointerdown", handler);

// DOM style
sprite.addEventListener(
  "click",
  (event) => {
    console.log("Clicked!", event.detail);
  },
  { once: true },
);

// Property-based handlers
sprite.onclick = (event) => {
  console.log("Clicked!", event.detail);
};
ts
import { Sprite } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";

// EventEmitter风格(推荐)
const handler = (e) => console.log("clicked");
sprite.on("pointerdown", handler);
sprite.once("pointerdown", handler); // 仅触发一次
sprite.off("pointerdown", handler);

// DOM风格
sprite.addEventListener(
  "click",
  (event) => {
    console.log("Clicked!", event.detail);
  },
  { once: true },
);

// 基于属性的处理器
sprite.onclick = (event) => {
  console.log("Clicked!", event.detail);
};

Pointer events and propagation

指针事件与传播

ts
import { Sprite, Container } from "pixi.js";

const parent = new Container();
parent.eventMode = "static";

const child = new Sprite();
child.eventMode = "static";
parent.addChild(child);

child.on("pointerdown", (event) => {
  console.log("child pressed");
  event.stopPropagation(); // prevent parent from receiving this event
});

parent.on("pointerdown", () => {
  console.log("parent pressed (only if child did not stop propagation)");
});
ts
import { Sprite, Container } from "pixi.js";

const parent = new Container();
parent.eventMode = "static";

const child = new Sprite();
child.eventMode = "static";
parent.addChild(child);

child.on("pointerdown", (event) => {
  console.log("child pressed");
  event.stopPropagation(); // 阻止父元素接收此事件
});

parent.on("pointerdown", () => {
  console.log("parent pressed (only if child did not stop propagation)");
});

Capture phase events

捕获阶段事件

All events support capture phase by appending
capture
to the event name (e.g.,
pointerdowncapture
,
clickcapture
). Capture listeners fire during the capturing phase, before the event reaches its target.
ts
container.addEventListener(
  "pointerdown",
  (event) => {
    event.stopImmediatePropagation(); // blocks event from reaching children
  },
  { capture: true },
);
所有事件都支持捕获阶段,只需在事件名称后添加
capture
即可(例如
pointerdowncapture
clickcapture
)。捕获监听器在捕获阶段触发,早于事件到达目标元素。
ts
container.addEventListener(
  "pointerdown",
  (event) => {
    event.stopImmediatePropagation(); // 阻止事件到达子元素
  },
  { capture: true },
);

Hit testing

命中测试

When a pointer event fires, PixiJS walks the display tree to find the top-most interactive element under the pointer. The traversal follows these rules:
  • eventMode = 'none'
    on a container skips that element and its entire subtree.
  • interactiveChildren = false
    on a container skips its children (the container itself can still be tested).
  • A
    hitArea
    overrides bounds-based testing; only the shape is checked.
  • Objects that are not visible, not renderable, or not measurable are skipped.
Set a custom
hitArea
to override bounds-based testing. This also speeds up hit tests on large or complex objects by reducing the geometry checked:
ts
import { Sprite, Rectangle, Circle, Polygon } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";

// Rectangular hit area
sprite.hitArea = new Rectangle(0, 0, 100, 50);

// Circular hit area
sprite.hitArea = new Circle(50, 50, 40);

// Polygon hit area
sprite.hitArea = new Polygon([0, 0, 100, 0, 50, 100]);

// Custom hit test via contains()
sprite.hitArea = {
  contains(x: number, y: number): boolean {
    return x >= 0 && x <= 100 && y >= 0 && y <= 100;
  },
};
当指针事件触发时,PixiJS会遍历显示树,找到指针下方最顶层的可交互元素。遍历遵循以下规则:
  • 容器的
    eventMode = 'none'
    会跳过该元素及其整个子树。
  • 容器的
    interactiveChildren = false
    会跳过其子元素(容器本身仍会被测试)。
  • hitArea
    会覆盖基于边界的测试;仅检查指定形状。
  • 不可见、不可渲染或不可测量的对象会被跳过。
设置自定义
hitArea
以覆盖基于边界的测试。这还能通过减少需要检查的几何图形,加快大型或复杂对象的命中测试速度:
ts
import { Sprite, Rectangle, Circle, Polygon } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";

// 矩形命中区域
sprite.hitArea = new Rectangle(0, 0, 100, 50);

// 圆形命中区域
sprite.hitArea = new Circle(50, 50, 40);

// 多边形命中区域
sprite.hitArea = new Polygon([0, 0, 100, 0, 50, 100]);

// 通过contains()自定义命中测试
sprite.hitArea = {
  contains(x: number, y: number): boolean {
    return x >= 0 && x <= 100 && y >= 0 && y <= 100;
  },
};

Global move events and drag

全局移动事件与拖拽

ts
import { Sprite, FederatedPointerEvent } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";
sprite.cursor = "grab";

let dragging = false;

sprite.on("pointerdown", (event: FederatedPointerEvent) => {
  dragging = true;
  sprite.cursor = "grabbing";
});

// globalpointermove fires even when pointer leaves the object
sprite.on("globalpointermove", (event: FederatedPointerEvent) => {
  if (dragging) {
    sprite.position.set(event.global.x, event.global.y);
  }
});

sprite.on("pointerup", () => {
  dragging = false;
  sprite.cursor = "grab";
});

sprite.on("pointerupoutside", () => {
  dragging = false;
  sprite.cursor = "grab";
});
ts
import { Sprite, FederatedPointerEvent } from "pixi.js";

const sprite = new Sprite();
sprite.eventMode = "static";
sprite.cursor = "grab";

let dragging = false;

sprite.on("pointerdown", (event: FederatedPointerEvent) => {
  dragging = true;
  sprite.cursor = "grabbing";
});

// globalpointermove即使在指针离开对象时也会触发
sprite.on("globalpointermove", (event: FederatedPointerEvent) => {
  if (dragging) {
    sprite.position.set(event.global.x, event.global.y);
  }
});

sprite.on("pointerup", () => {
  dragging = false;
  sprite.cursor = "grab";
});

sprite.on("pointerupoutside", () => {
  dragging = false;
  sprite.cursor = "grab";
});

Cursor styles

光标样式

Basic usage sets the
cursor
property per-object. For reusable cursors, register named styles on the event system:
ts
app.renderer.events.cursorStyles.default = "url('bunny.png'), auto";
app.renderer.events.cursorStyles.hover = "url('bunny_saturated.png'), auto";

sprite.eventMode = "static";
sprite.cursor = "hover"; // uses the registered 'hover' style
Cursor styles can be strings (CSS cursor values), objects (applied as CSS styles), or functions (called with the mode string).
基本用法是为每个对象设置
cursor
属性。如需可复用的光标样式,请在事件系统上注册命名样式:
ts
app.renderer.events.cursorStyles.default = "url('bunny.png'), auto";
app.renderer.events.cursorStyles.hover = "url('bunny_saturated.png'), auto";

sprite.eventMode = "static";
sprite.cursor = "hover"; // 使用已注册的'hover'样式
光标样式可以是字符串(CSS光标值)、对象(作为CSS样式应用)或函数(使用模式字符串调用)。

Event properties

事件属性

FederatedPointerEvent
carries rich input data; the more useful fields are:
ts
sprite.on("pointerdown", (event: FederatedPointerEvent) => {
  event.global; // scene-space Point where the event happened
  event.client; // CSS-pixel Point relative to the viewport
  event.offset; // Point w.r.t. target Container in world space (not supported at the moment)
  event.target; // the Container that received the event
  event.currentTarget; // the Container whose listener is running

  event.pointerType; // 'mouse' | 'pen' | 'touch'
  event.pointerId; // unique id for multi-touch tracking
  event.isPrimary; // first pointer in a multi-pointer gesture
  event.pressure; // 0-1 pen/touch pressure
  event.button; // 0 left, 1 middle, 2 right
  event.buttons; // bitmask of held buttons
  event.altKey; // modifier key state
  event.ctrlKey;
  event.shiftKey;
  event.metaKey;

  event.nativeEvent; // the underlying DOM PointerEvent / MouseEvent / Touch
  event.preventDefault();
  event.stopPropagation();
  event.stopImmediatePropagation();
});
FederatedWheelEvent
adds
deltaX
,
deltaY
,
deltaZ
, and
deltaMode
. Wheel events fire on the same hit-tested object as pointer events.
FederatedPointerEvent
携带丰富的输入数据,常用字段如下:
ts
sprite.on("pointerdown", (event: FederatedPointerEvent) => {
  event.global; // 事件发生的场景空间Point
  event.client; // 相对于视口的CSS像素Point
  event.offset; // 相对于目标Container的世界空间Point(目前不支持)
  event.target; // 接收事件的Container
  event.currentTarget; // 运行监听器的Container

  event.pointerType; // 'mouse' | 'pen' | 'touch'
  event.pointerId; // 用于多点触控追踪的唯一ID
  event.isPrimary; // 多点触控手势中的第一个指针
  event.pressure; // 0-1范围的笔/触摸压力
  event.button; // 0=左键,1=中键,2=右键
  event.buttons; // 按住按钮的位掩码
  event.altKey; // 修饰键状态
  event.ctrlKey;
  event.shiftKey;
  event.metaKey;

  event.nativeEvent; // 底层DOM PointerEvent / MouseEvent / Touch
  event.preventDefault();
  event.stopPropagation();
  event.stopImmediatePropagation();
});
FederatedWheelEvent
新增了
deltaX
deltaY
deltaZ
deltaMode
。滚轮事件与指针事件在同一个命中测试对象上触发。

Event features

事件特性

Toggle event categories globally for performance:
ts
await app.init({
  eventFeatures: {
    move: true, // pointer/mouse/touch move events
    globalMove: true, // global move events (globalpointermove, etc.)
    click: true, // click/tap/press events
    wheel: true, // mouse wheel events
  },
});

// or configure after init
app.renderer.events.features.globalMove = false;
可全局切换事件类别以提升性能:
ts
await app.init({
  eventFeatures: {
    move: true, // 指针/鼠标/触摸移动事件
    globalMove: true, // 全局移动事件(globalpointermove等)
    click: true, // click/tap/press事件
    wheel: true, // 鼠标滚轮事件
  },
});

// 或在初始化后配置
app.renderer.events.features.globalMove = false;

Performance tips

性能提示

  • Set
    eventMode = 'none'
    on non-interactive subtrees to skip hit testing entirely.
  • Set
    interactiveChildren = false
    on containers where only the container itself needs interaction.
  • Use
    hitArea
    on large or complex objects to replace bounds-based hit testing with a cheap shape check.
  • Prefer
    'static'
    for stationary elements; reserve
    'dynamic'
    for objects that move or animate under a stationary pointer.
  • Disable unused event features via
    eventFeatures
    (e.g.,
    globalMove: false
    ) to cut per-frame work.
  • 对非交互子树设置
    eventMode = 'none'
    ,以完全跳过命中测试。
  • 对仅容器本身需要交互的容器设置
    interactiveChildren = false
  • 对大型或复杂对象使用
    hitArea
    ,用低成本的形状检查替代基于边界的命中测试。
  • 对静止元素优先使用
    'static'
    ;仅当对象在静止光标下移动或动画时才保留
    'dynamic'
  • 通过
    eventFeatures
    禁用未使用的事件特性(例如
    globalMove: false
    ),减少每帧的工作量。

Common Mistakes

常见错误

[HIGH] Default eventMode is passive

[高风险] 默认eventMode为passive

Wrong:
ts
const sprite = new Sprite(texture);
sprite.on("pointerdown", () => {
  console.log("clicked");
});
Correct:
ts
const sprite = new Sprite(texture);
sprite.eventMode = "static";
sprite.on("pointerdown", () => {
  console.log("clicked");
});
The default
eventMode
is
'passive'
, which means the object itself receives no events. You must explicitly set
eventMode
to
'static'
or
'dynamic'
before any listener will fire.
错误写法:
ts
const sprite = new Sprite(texture);
sprite.on("pointerdown", () => {
  console.log("clicked");
});
正确写法:
ts
const sprite = new Sprite(texture);
sprite.eventMode = "static";
sprite.on("pointerdown", () => {
  console.log("clicked");
});
默认的
eventMode
'passive'
,这意味着对象本身不会接收任何事件。你必须显式将
eventMode
设置为
'static'
'dynamic'
,监听器才会触发。

[HIGH] buttonMode removed; use cursor

[高风险] buttonMode已移除;请使用cursor

Wrong:
ts
sprite.interactive = true;
sprite.buttonMode = true;
Correct:
ts
sprite.eventMode = "static";
sprite.cursor = "pointer";
buttonMode
was removed in v8. Use
cursor = 'pointer'
to show a hand cursor on hover.
interactive = true
still works as an alias for
eventMode = 'static'
, but
eventMode
is preferred.
错误写法:
ts
sprite.interactive = true;
sprite.buttonMode = true;
正确写法:
ts
sprite.eventMode = "static";
sprite.cursor = "pointer";
buttonMode
在v8中已被移除。使用
cursor = 'pointer'
在悬停时显示手型光标。
interactive = true
仍可作为
eventMode = 'static'
的别名,但推荐使用
eventMode

[HIGH] Move events only fire over the object in v8

[高风险] v8中移动事件仅在对象上方触发

Wrong:
ts
sprite.eventMode = "static";
sprite.on("pointermove", (event) => {
  // expects to fire everywhere; only fires inside sprite bounds
  updateDrag(event.global.x, event.global.y);
});
Correct:
ts
sprite.eventMode = "static";
sprite.on("globalpointermove", (event) => {
  // fires everywhere, even outside sprite bounds
  updateDrag(event.global.x, event.global.y);
});
In v8,
pointermove
,
mousemove
, and
touchmove
only fire when the pointer is over the display object. In v7 they fired on any canvas move. For drag operations or global tracking, use
globalpointermove
,
globalmousemove
, or
globaltouchmove
.
错误写法:
ts
sprite.eventMode = "static";
sprite.on("pointermove", (event) => {
  // 期望在任何位置触发;但仅在sprite边界内触发
  updateDrag(event.global.x, event.global.y);
});
正确写法:
ts
sprite.eventMode = "static";
sprite.on("globalpointermove", (event) => {
  // 在任何位置触发,即使在sprite边界外
  updateDrag(event.global.x, event.global.y);
});
在v8中,
pointermove
mousemove
touchmove
仅在指针位于显示对象上方时触发。在v7中,它们在画布上的任何移动都会触发。如需拖拽操作或全局追踪,请使用
globalpointermove
globalmousemove
globaltouchmove

[MEDIUM] Cursor does not inherit from parent

[中风险] 光标不会从父元素继承

Setting
cursor
on a parent container has no effect on its children. Only the direct hit target's
cursor
value is applied.
ts
// This does NOT make children show a pointer cursor
parent.cursor = "pointer";

// Each interactive child needs its own cursor
child.eventMode = "static";
child.cursor = "pointer";
If you want a uniform cursor for all children, set
cursor
on each interactive child individually, or set
hitArea
on the parent and make children non-interactive.
在父容器上设置
cursor
对子元素没有影响。仅直接命中目标的
cursor
值会生效。
ts
// 这不会让子元素显示指针光标
parent.cursor = "pointer";

// 每个可交互子元素都需要单独设置cursor
child.eventMode = "static";
child.cursor = "pointer";
如果希望所有子元素使用统一的光标,请为每个可交互子元素单独设置
cursor
,或者在父元素上设置
hitArea
并将子元素设为非交互。

API Reference

API参考