pixijs-accessibility

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Enable screen reader and keyboard navigation via PixiJS's AccessibilitySystem. The system creates an invisible shadow DOM overlay positioned over accessible containers so assistive technology can discover and activate them.
通过PixiJS的AccessibilitySystem启用屏幕阅读器和键盘导航功能。该系统会在可访问容器上方创建一个不可见的Shadow DOM覆盖层,以便辅助技术能够发现并激活这些容器。

Quick Start

快速入门

ts
const button = new Sprite(await Assets.load("button.png"));
button.accessible = true;
button.accessibleTitle = "Play game";
button.accessibleHint = "Starts a new game session";
button.eventMode = "static";
button.tabIndex = 0;
app.stage.addChild(button);

app.renderer.accessibility.setAccessibilityEnabled(true);

button.on("pointertap", () => startGame());
Related skills:
pixijs-events
(pointer/tap handlers),
pixijs-scene-dom-container
(HTML elements on canvas),
pixijs-application
(init options).
Key points:
  • By default the system activates only after the user presses Tab. Set
    enabledByDefault: true
    in Application init for immediate activation.
  • On mobile, the system creates a hidden touch hook; screen-reader focus activates accessibility for the whole session.
  • The AccessibilitySystem requires the main thread; it is not available in a Web Worker.
ts
const button = new Sprite(await Assets.load("button.png"));
button.accessible = true;
button.accessibleTitle = "Play game";
button.accessibleHint = "Starts a new game session";
button.eventMode = "static";
button.tabIndex = 0;
app.stage.addChild(button);

app.renderer.accessibility.setAccessibilityEnabled(true);

button.on("pointertap", () => startGame());
相关技能:
pixijs-events
(指针/点击处理器)、
pixijs-scene-dom-container
(画布上的HTML元素)、
pixijs-application
(初始化选项)。
关键点:
  • 默认情况下,系统仅在用户按下Tab键后激活。在Application初始化时设置
    enabledByDefault: true
    可实现立即激活。
  • 在移动端,系统会创建一个隐藏的触摸钩子;屏幕阅读器获得焦点后,会在整个会话期间保持无障碍功能激活。
  • AccessibilitySystem需要在主线程运行;Web Worker环境下不可用。

Core Patterns

核心模式

Container accessible properties

容器无障碍属性

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

const container = new Container();
container.accessible = true;
container.accessibleTitle = "Navigation menu";
container.accessibleHint = "Contains links to other pages";
container.eventMode = "static"; // required for custom tabIndex to apply
container.tabIndex = 0;
container.accessibleType = "div"; // defaults to 'button'

const sprite = new Sprite();
sprite.accessible = true;
sprite.accessibleTitle = "Close dialog";
sprite.accessibleText = "X"; // text content of the shadow div
sprite.eventMode = "static";
sprite.tabIndex = 1;
Available properties on any Container:
  • accessible
    (boolean) - enables the accessible overlay div
  • accessibleTitle
    (string) - sets the
    title
    attribute on the shadow div
  • accessibleHint
    (string) - sets the
    aria-label
    attribute
  • accessibleText
    (string) - sets inner text content of the shadow div
  • accessibleType
    (string) - HTML tag for the shadow element, defaults to
    'button'
  • tabIndex
    (number) - tab order for keyboard navigation (only applied when
    interactive
    is true /
    eventMode
    is
    'static'
    or
    'dynamic'
    )
  • accessibleChildren
    (boolean, default
    true
    ) - when
    false
    , prevents child containers from being accessible
  • accessiblePointerEvents
    (string) - CSS
    pointer-events
    value on the shadow div
ts
import { Container, Sprite } from "pixi.js";

const container = new Container();
container.accessible = true;
container.accessibleTitle = "Navigation menu";
container.accessibleHint = "Contains links to other pages";
container.eventMode = "static"; // 自定义tabIndex生效的必要设置
container.tabIndex = 0;
container.accessibleType = "div"; // 默认值为'button'

const sprite = new Sprite();
sprite.accessible = true;
sprite.accessibleTitle = "Close dialog";
sprite.accessibleText = "X"; // Shadow div的文本内容
sprite.eventMode = "static";
sprite.tabIndex = 1;
所有Container可用的属性:
  • accessible
    (布尔值)- 启用可访问覆盖层div
  • accessibleTitle
    (字符串)- 设置Shadow div的
    title
    属性
  • accessibleHint
    (字符串)- 设置
    aria-label
    属性
  • accessibleText
    (字符串)- 设置Shadow div的内部文本内容
  • accessibleType
    (字符串)- Shadow元素的HTML标签,默认值为
    'button'
  • tabIndex
    (数字)- 键盘导航的Tab顺序(仅当
    interactive
    为true /
    eventMode
    'static'
    'dynamic'
    时生效)
  • accessibleChildren
    (布尔值,默认
    true
    )- 设置为
    false
    时,子容器将不可访问
  • accessiblePointerEvents
    (字符串)- Shadow div的CSS
    pointer-events

Custom tab order

自定义Tab顺序

Give each accessible container a
tabIndex
to control the order assistive tech walks through them. Higher numbers come later; equal numbers fall back to scene-graph order.
ts
menuButton.accessible = true;
menuButton.eventMode = "static";
menuButton.tabIndex = 1;

playButton.accessible = true;
playButton.eventMode = "static";
playButton.tabIndex = 2;

settingsButton.accessible = true;
settingsButton.eventMode = "static";
settingsButton.tabIndex = 3;
tabIndex
is only forwarded to the shadow div when the container is
interactive
(
eventMode
is
'static'
or
'dynamic'
). Without that, the system clamps the div's tabIndex back to
0
, and the order you set is ignored.
为每个可访问容器设置
tabIndex
,以控制辅助技术遍历它们的顺序。数值越大,顺序越靠后;数值相同时,回退到场景图顺序。
ts
menuButton.accessible = true;
menuButton.eventMode = "static";
menuButton.tabIndex = 1;

playButton.accessible = true;
playButton.eventMode = "static";
playButton.tabIndex = 2;

settingsButton.accessible = true;
settingsButton.eventMode = "static";
settingsButton.tabIndex = 3;
只有当容器处于
interactive
状态(
eventMode
'static'
'dynamic'
)时,
tabIndex
才会传递给Shadow div。否则,系统会将div的tabIndex重置为
0
,你设置的顺序将被忽略。

Programmatic control

程序化控制

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

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

// Enable accessibility at runtime
app.renderer.accessibility.setAccessibilityEnabled(true);

// Check current state
console.log(app.renderer.accessibility.isActive);
console.log(app.renderer.accessibility.isMobileAccessibility);

// Full init options:
await app.init({
  accessibilityOptions: {
    enabledByDefault: true, // activate immediately (default: false)
    debug: true, // makes overlay divs visible (default: false)
    activateOnTab: true, // Tab key activates system (default: true)
    deactivateOnMouseMove: false, // stay active when mouse moves (default: true)
  },
});
The system can also be configured via static defaults before creating the Application:
ts
import { AccessibilitySystem, Application } from "pixi.js";

AccessibilitySystem.defaultOptions.enabledByDefault = true;
AccessibilitySystem.defaultOptions.deactivateOnMouseMove = false;

const app = new Application();
await app.init();
ts
import { Application } from "pixi.js";

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

// 运行时启用无障碍功能
app.renderer.accessibility.setAccessibilityEnabled(true);

// 检查当前状态
console.log(app.renderer.accessibility.isActive);
console.log(app.renderer.accessibility.isMobileAccessibility);

// 完整初始化选项:
await app.init({
  accessibilityOptions: {
    enabledByDefault: true, // 立即激活(默认值:false)
    debug: true, // 使覆盖层div可见(默认值:false)
    activateOnTab: true, // Tab键激活系统(默认值:true)
    deactivateOnMouseMove: false, // 鼠标移动时保持激活状态(默认值:true)
  },
});
也可以在创建Application之前,通过静态默认值配置系统:
ts
import { AccessibilitySystem, Application } from "pixi.js";

AccessibilitySystem.defaultOptions.enabledByDefault = true;
AccessibilitySystem.defaultOptions.deactivateOnMouseMove = false;

const app = new Application();
await app.init();

Handling accessible interactions

处理可访问交互

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

const button = new Sprite();
button.eventMode = "static";
button.accessible = true;
button.accessibleTitle = "Submit form";
button.tabIndex = 0;

// Screen readers trigger click/tap events through the shadow DOM element
button.on("pointertap", () => {
  submitForm();
});
When accessibility is active and a user activates a shadow div (via Enter/Space key or screen reader action), the system dispatches
click
,
pointertap
, and
tap
FederatedEvents to the corresponding container. Focus on the shadow div dispatches
mouseover
, and focus-out dispatches
mouseout
. Both
eventMode
and
accessible
should be set for full keyboard + pointer support.
ts
import { Sprite } from "pixi.js";

const button = new Sprite();
button.eventMode = "static";
button.accessible = true;
button.accessibleTitle = "Submit form";
button.tabIndex = 0;

// 屏幕阅读器通过Shadow DOM元素触发点击/轻触事件
button.on("pointertap", () => {
  submitForm();
});
当无障碍功能激活,且用户激活Shadow div(通过Enter/Space键或屏幕阅读器操作)时,系统会向对应的容器派发
click
pointertap
tap
FederatedEvents。Shadow div获得焦点时会派发
mouseover
,失去焦点时派发
mouseout
。要获得完整的键盘+指针支持,需同时设置
eventMode
accessible

Common Mistakes

常见误区

[MEDIUM] Expecting accessibility to be active without Tab key press

[中等] 认为无需按Tab键即可激活无障碍功能

The AccessibilitySystem does not create its DOM overlay until the user presses Tab (or, on mobile, focuses the touch hook). If your application needs accessibility immediately:
ts
const app = new Application();
await app.init({
  accessibilityOptions: {
    enabledByDefault: true,
  },
});
Or at runtime:
ts
app.renderer.accessibility.setAccessibilityEnabled(true);
Without one of these, automated accessibility testing tools will not find the overlay elements.
AccessibilitySystem在用户按下Tab键(或在移动端聚焦触摸钩子)之前,不会创建DOM覆盖层。如果你的应用需要立即启用无障碍功能:
ts
const app = new Application();
await app.init({
  accessibilityOptions: {
    enabledByDefault: true,
  },
});
或在运行时设置:
ts
app.renderer.accessibility.setAccessibilityEnabled(true);
如果不进行上述任一设置,自动化无障碍测试工具将无法找到覆盖层元素。

[MEDIUM] Setting accessible without accessibleTitle

[中等] 设置了accessible但未设置accessibleTitle

Wrong:
ts
const sprite = new Sprite();
sprite.accessible = true;
// no title or hint set
Correct:
ts
const sprite = new Sprite();
sprite.accessible = true;
sprite.accessibleTitle = "Play button";
sprite.accessibleHint = "Click to start the game";
A container with
accessible = true
but no
accessibleTitle
or
accessibleHint
gets a fallback title of
"container {tabIndex}"
. Screen readers will announce this generic label with no useful context. Always provide at least
accessibleTitle
.
错误示例:
ts
const sprite = new Sprite();
sprite.accessible = true;
// 未设置标题或提示
正确示例:
ts
const sprite = new Sprite();
sprite.accessible = true;
sprite.accessibleTitle = "Play button";
sprite.accessibleHint = "Click to start the game";
设置
accessible = true
但未设置
accessibleTitle
accessibleHint
的容器,会使用
"container {tabIndex}"
作为默认标题。屏幕阅读器会播报这个通用标签,没有任何有用的上下文信息。请始终至少提供
accessibleTitle

[MEDIUM] Accessibility deactivates when moving mouse

[中等] 移动鼠标时无障碍功能失效

By default,
deactivateOnMouseMove
is
true
. Any mouse movement after Tab-activation will deactivate the overlay. This is by design (assumes keyboard-only users don't use a mouse), but it makes testing with a mouse frustrating.
ts
await app.init({
  accessibilityOptions: {
    deactivateOnMouseMove: false,
  },
});
默认情况下,
deactivateOnMouseMove
true
。通过Tab键激活后,任何鼠标移动都会使覆盖层失效。这是设计使然(假设仅使用键盘的用户不会使用鼠标),但会给鼠标测试带来不便。
ts
await app.init({
  accessibilityOptions: {
    deactivateOnMouseMove: false,
  },
});

[MEDIUM] Not importing accessibility extension in custom builds

[中等] 自定义构建时未导入无障碍扩展

When using
skipExtensionImports: true
for a custom build, the accessibility extension is not automatically registered. You must import it explicitly:
ts
import "pixi.js/accessibility";
import { Application } from "pixi.js";

const app = new Application();
await app.init({ skipExtensionImports: true });
Without this import,
app.renderer.accessibility
will be undefined and no shadow DOM layer will be created.
当自定义构建使用
skipExtensionImports: true
时,无障碍扩展不会自动注册。你必须显式导入它:
ts
import "pixi.js/accessibility";
import { Application } from "pixi.js";

const app = new Application();
await app.init({ skipExtensionImports: true });
如果不进行此导入,
app.renderer.accessibility
将为undefined,且不会创建Shadow DOM层。

API Reference

API参考