mcpField
P
pixijs-accessibility

pixijs-accessibility

github.com/pixijs设计与 UI3585.6k 次安装4 周前更新
代码仓库

关于 pixijs-accessibility

通过 PixiJS 的 AccessibilitySystem 为画布内容添加屏幕阅读器和键盘辅助功能。内容涵盖可访问标签、Tab 顺序、交互触发方式及运行时配置。

主要功能

  1. 01向屏幕阅读器公开容器
  2. 02设置可访问标题与提示
  3. 03控制键盘 Tab 顺序
  4. 04分发辅助交互事件
  5. 05配置辅助功能的启用方式

适用场景

  1. 01让画布按钮支持键盘操作
  2. 02为场景元素添加屏幕阅读器标签
  3. 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)- 启用无障碍覆盖 div
  • accessibleTitle(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 上的 CSS pointer-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 层。

API Reference

评论

支持 Markdown