pixijs-events
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePixiJS's federated event system mirrors DOM events on the scene graph. Set to opt an object in, then listen with , , or property handlers. Move events fire only over the listening object; use for drag.
container.eventMode = 'static'.on()addEventListener()onEventNameglobalpointermovePixiJS的联合事件系统在场景图上镜像DOM事件。设置将对象设为可交互,然后通过、或属性处理器监听事件。移动事件仅在监听对象上方触发;如需拖拽功能,请使用。
container.eventMode = 'static'.on()addEventListener()onEventNameglobalpointermoveQuick 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: (screen reader + keyboard), (HTML overlays), (event-heavy scenes).
pixijs-accessibilitypixijs-scene-dom-containerpixijs-performancets
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);
});相关技能: (屏幕阅读器+键盘)、(HTML叠加层)、(事件密集型场景)。
pixijs-accessibilitypixijs-scene-dom-containerpixijs-performanceCore 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 for buttons, UI elements, and drag targets. Use only for objects that move under a stationary cursor and need continuous hover updates.
'static''dynamic'Use to check whether an object can receive events:
isInteractive()ts
sprite.eventMode = "static";
sprite.isInteractive(); // true
sprite.eventMode = "passive";
sprite.isInteractive(); // falsets
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";将用于按钮、UI元素和拖拽目标。仅当对象在静止光标下移动且需要持续悬停更新时,才使用。
'static''dynamic'使用检查对象是否能接收事件:
isInteractive()ts
sprite.eventMode = "static";
sprite.isInteractive(); // true
sprite.eventMode = "passive";
sprite.isInteractive(); // falseEvent types
事件类型
Pointer events (recommended for cross-device compatibility): , , , , , , , , , .
pointerdownpointeruppointerupoutsidepointermovepointeroverpointeroutpointerenterpointerleavepointertappointercancelMouse events: , , , , , , , , , , , , , .
mousedownmouseupmouseupoutsidemousemovemouseovermouseoutmouseentermouseleaveclickrightdownrightuprightupoutsiderightclickwheelTouch events: , , , , , . Each touch carries , , , and copied from the native , so modifier keys work the same as with mouse or pointer events.
touchstarttouchendtouchendoutsidetouchmovetouchcanceltapaltKeyctrlKeymetaKeyshiftKeyTouchEventGlobal move events: , , . These fire on every pointer movement regardless of whether the pointer is over the listening object.
globalpointermoveglobalmousemoveglobaltouchmoveContainer lifecycle events (no required): , , , , , .
eventModeaddedremoveddestroyedchildAddedchildRemovedvisibleChanged指针事件(推荐用于跨设备兼容性):、、、、、、、、、。
pointerdownpointeruppointerupoutsidepointermovepointeroverpointeroutpointerenterpointerleavepointertappointercancel鼠标事件:、、、、、、、、、、、、、。
mousedownmouseupmouseupoutsidemousemovemouseovermouseoutmouseentermouseleaveclickrightdownrightuprightupoutsiderightclickwheel触摸事件:、、、、、。每个触摸事件都会复制原生的、、和,因此修饰键的工作方式与鼠标或指针事件相同。
touchstarttouchendtouchendoutsidetouchmovetouchcanceltapTouchEventaltKeyctrlKeymetaKeyshiftKey全局移动事件:、、。无论指针是否在监听对象上方,这些事件都会在每次指针移动时触发。
globalpointermoveglobalmousemoveglobaltouchmove容器生命周期事件(无需设置):、、、、、。
eventModeaddedremoveddestroyedchildAddedchildRemovedvisibleChangedListening 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 to the event name (e.g., , ). Capture listeners fire during the capturing phase, before the event reaches its target.
capturepointerdowncaptureclickcapturets
container.addEventListener(
"pointerdown",
(event) => {
event.stopImmediatePropagation(); // blocks event from reaching children
},
{ capture: true },
);所有事件都支持捕获阶段,只需在事件名称后添加即可(例如、)。捕获监听器在捕获阶段触发,早于事件到达目标元素。
capturepointerdowncaptureclickcapturets
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:
- on a container skips that element and its entire subtree.
eventMode = 'none' - on a container skips its children (the container itself can still be tested).
interactiveChildren = false - A overrides bounds-based testing; only the shape is checked.
hitArea - Objects that are not visible, not renderable, or not measurable are skipped.
Set a custom to override bounds-based testing. This also speeds up hit tests on large or complex objects by reducing the geometry checked:
hitAreats
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 - 不可见、不可渲染或不可测量的对象会被跳过。
设置自定义以覆盖基于边界的测试。这还能通过减少需要检查的几何图形,加快大型或复杂对象的命中测试速度:
hitAreats
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 property per-object. For reusable cursors, register named styles on the event system:
cursorts
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' styleCursor styles can be strings (CSS cursor values), objects (applied as CSS styles), or functions (called with the mode string).
基本用法是为每个对象设置属性。如需可复用的光标样式,请在事件系统上注册命名样式:
cursorts
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
事件属性
FederatedPointerEventts
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();
});FederatedWheelEventdeltaXdeltaYdeltaZdeltaModeFederatedPointerEventts
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();
});FederatedWheelEventdeltaXdeltaYdeltaZdeltaModeEvent 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 on non-interactive subtrees to skip hit testing entirely.
eventMode = 'none' - Set on containers where only the container itself needs interaction.
interactiveChildren = false - Use on large or complex objects to replace bounds-based hit testing with a cheap shape check.
hitArea - Prefer for stationary elements; reserve
'static'for objects that move or animate under a stationary pointer.'dynamic' - Disable unused event features via (e.g.,
eventFeatures) to cut per-frame work.globalMove: false
- 对非交互子树设置,以完全跳过命中测试。
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 is , which means the object itself receives no events. You must explicitly set to or before any listener will fire.
eventMode'passive'eventMode'static''dynamic'错误写法:
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";buttonModecursor = 'pointer'interactive = trueeventMode = 'static'eventMode错误写法:
ts
sprite.interactive = true;
sprite.buttonMode = true;正确写法:
ts
sprite.eventMode = "static";
sprite.cursor = "pointer";buttonModecursor = 'pointer'interactive = trueeventMode = '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, , , and 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 , , or .
pointermovemousemovetouchmoveglobalpointermoveglobalmousemoveglobaltouchmove错误写法:
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中,、和仅在指针位于显示对象上方时触发。在v7中,它们在画布上的任何移动都会触发。如需拖拽操作或全局追踪,请使用、或。
pointermovemousemovetouchmoveglobalpointermoveglobalmousemoveglobaltouchmove[MEDIUM] Cursor does not inherit from parent
[中风险] 光标不会从父元素继承
Setting on a parent container has no effect on its children. Only the direct hit target's value is applied.
cursorcursorts
// 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 on each interactive child individually, or set on the parent and make children non-interactive.
cursorhitArea在父容器上设置对子元素没有影响。仅直接命中目标的值会生效。
cursorcursorts
// 这不会让子元素显示指针光标
parent.cursor = "pointer";
// 每个可交互子元素都需要单独设置cursor
child.eventMode = "static";
child.cursor = "pointer";如果希望所有子元素使用统一的光标,请为每个可交互子元素单独设置,或者在父元素上设置并将子元素设为非交互。
cursorhitArea