关于 pixijs-accessibility
通过 PixiJS 的 AccessibilitySystem 为画布内容添加屏幕阅读器和键盘辅助功能。内容涵盖可访问标签、Tab 顺序、交互触发方式及运行时配置。
主要功能
- 01向屏幕阅读器公开容器
- 02设置可访问标题与提示
- 03控制键盘 Tab 顺序
- 04分发辅助交互事件
- 05配置辅助功能的启用方式
适用场景
- 01让画布按钮支持键盘操作
- 02为场景元素添加屏幕阅读器标签
- 03测试辅助功能覆盖层
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pixijs/ pixijs-skills --skill pixijs-accessibility安装后在 Claude Code 中输入/pixijs-accessibility确认已加载。
详细介绍
通过 PixiJS 的 AccessibilitySystem 启用屏幕阅读器支持和键盘导航。该系统会在可访问容器上方创建一个不可见的 shadow DOM 覆盖层,让辅助技术能够发现并激活这些容器。
快速入门
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 中不可用。
核心模式
容器的无障碍属性
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;
任何 Container 都可使用以下属性:
accessible(boolean)- 启用无障碍覆盖 divaccessibleTitle(string)- 设置 shadow div 上的title属性accessibleHint(string)- 设置aria-label属性accessibleText(string)- 设置 shadow div 的内部文本内容accessibleType(string)- shadow 元素的 HTML 标签,默认为'button'tabIndex(number)- 键盘导航的 Tab 顺序(仅当interactive为true/eventMode为'static'或'dynamic'时才会应用)accessibleChildren(boolean,默认为true)- 设为false时,阻止子容器变为可访问accessiblePointerEvents(string)- shadow div 上的 CSSpointer-events值
自定义 Tab 顺序
为每个可访问容器设置 tabIndex,以控制辅助技术遍历这些容器的顺序。数字越大,顺序越靠后;数字相同则按场景图顺序排列。
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,并忽略你设置的顺序。
编程控制
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)
},
});
也可以在创建 Application 之前,通过静态默认值配置系统:
import { AccessibilitySystem, Application } from "pixi.js";
AccessibilitySystem.defaultOptions.enabledByDefault = true;
AccessibilitySystem.defaultOptions.deactivateOnMouseMove = false;
const app = new Application();
await app.init();
处理无障碍交互
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();
});
启用无障碍功能后,用户激活 shadow div(通过 Enter/Space 键或屏幕阅读器操作)时,系统会向对应的容器分发 click、pointertap 和 tap FederatedEvents。shadow div 获得焦点时会分发 mouseover,失去焦点时会分发 mouseout。要完整支持键盘和指针交互,应同时设置 eventMode 和 accessible。
常见错误
[MEDIUM] 期望无障碍功能在未按 Tab 键时就已启用
用户按下 Tab 后(或在移动设备上聚焦触摸钩子后),AccessibilitySystem 才会创建 DOM 覆盖层。如果应用需要立即启用无障碍功能:
const app = new Application();
await app.init({
accessibilityOptions: {
enabledByDefault: true,
},
});
或者在运行时:
app.renderer.accessibility.setAccessibilityEnabled(true);
如果不进行上述任一设置,自动化无障碍测试工具将无法找到覆盖层元素。
[MEDIUM] 设置 accessible 却未设置 accessibleTitle
错误:
const sprite = new Sprite();
sprite.accessible = true;
// no title or hint set
正确:
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] 移动鼠标时无障碍功能停用
默认情况下,deactivateOnMouseMove 为 true。Tab 激活后,任何鼠标移动都会停用覆盖层。这是有意设计的(假定仅使用键盘的用户不会使用鼠标),但会让使用鼠标进行测试变得很不方便。
await app.init({
accessibilityOptions: {
deactivateOnMouseMove: false,
},
});
[MEDIUM] 自定义构建中未导入无障碍扩展
使用 skipExtensionImports: true 进行自定义构建时,不会自动注册无障碍扩展。必须显式导入:
import "pixi.js/accessibility";
import { Application } from "pixi.js";
const app = new Application();
await app.init({ skipExtensionImports: true });
如果未导入,app.renderer.accessibility 将为 undefined,也不会创建 shadow DOM 层。