返回

A-Frame自定义字体失效问题:JavaScript加载修复方案

javascript

A-Frame 中使用 JavaScript 加载自定义字体失效问题解析

A-Frame 是一款用于构建虚拟现实 (VR) 体验的 Web 框架。开发者可借助 A-Frame 的声明式特性,通过 HTML 标记构建 VR 场景。在实际项目中,可能会遇到需定制字体的需求。本文章针对使用 JavaScript 创建 A-Frame 实体并应用自定义字体时失效问题,进行详细解释并给出对应的解决方案。

问题

当直接在 HTML 标签内使用自定义字体来渲染含有特殊字符(Non-ASCII Characters)的文本时,渲染正常。如通过如下代码,渲染出包含ñ字符的单词Extraño

<a-entity
          geometry="primitive: plane; width: 4; height: 0"
          material="visible: false"          
          text="value: Extraño; color: blue; font: custom-msdf.json; font-image: custom-msdf.png; negate: false"
          position="0 2 -2"></a-entity>

但如果使用 JavaScript 动态创建实体,并尝试用 setAttribute 方法设置 text 组件的 value 属性为包含特殊字符(Non-ASCII Characters)的文本,则自定义字体渲染失效,特殊字符无法正确显示。用纯 ASCII 字符渲染 English 单词也会有这个问题。代码示例如下:

const titleEntity = document.createElement('a-entity');
titleEntity.setAttribute('class', 'title');
titleEntity.setAttribute('geometry', 'primitive: plane; height: 0.02; width: 0.23');
titleEntity.setAttribute('material', 'visible:false');
titleEntity.setAttribute('position', '0 0.01 0.076');
titleEntity.setAttribute('rotation', '0 0 90');
titleEntity.setAttribute('negate', 'false');
titleEntity.setAttribute('text', `value:Extraño; font: custom-msdf.json; font-image: custom-msdf.png; negate: false`);
// or titleEntity.setAttribute('text', `value:English;`);

原因分析

根本原因在于通过 setAttribute 方法设置的组件属性时,A-Frame 内部的组件初始化和数据解析机制与 HTML 标签方式不同,会触发一个重复处理的问题。当组件的属性是通过 Javascript 的 setAttribute 的形式来设置时,属性的解析和实体构建流程将会被不正确地触发两次。这个组件重复构建两次的问题导致了某些非 ASCII 字符在 Javascript 方式创建自定义字体实体时显示失效,也导致了即使使用纯 ASCII 字符,渲染也会出现错误。这个问题可以通过更新 A-Frame 版本修复,但更好的方式是采用更规范和可控的方式构建组件。

解决方案

方案一:使用组件的属性对象进行设置

此方案的核心是,不要再利用 setAttribute 去设置 text 的属性,而是通过先构建 text 属性的结构对象,之后再用 setAttribute 将这整个组件添加到 a-entity 元素。这将允许字体资源的正常加载,且自定义的字体属性得以应用。这能够有效避免自定义字体的不正确解析问题,是更可控的一种操作方式。
通过这种构建方法,即可使 A-frame 更稳健地兼容新的和遗留的实现,有效降低项目的技术栈迁移风险,并可提升性能。

操作步骤:

  1. 创建一个对象,用于存放 text 组件的各个属性。
  2. 将各个属性分别添加到这个对象中。例如:valuefontfont-image 等。
  3. 将 text 属性对象设置到 A-Frame 实体上。

代码示例:

const titleEntity = document.createElement('a-entity');
titleEntity.setAttribute('class', 'title');
titleEntity.setAttribute('geometry', 'primitive: plane; height: 0.02; width: 0.23');
titleEntity.setAttribute('material', 'visible:false');
titleEntity.setAttribute('position', '0 0.01 0.076');
titleEntity.setAttribute('rotation', '0 0 90');
titleEntity.setAttribute('negate', 'false');

// 创建 text 属性对象
const textProperties = {
  value: 'Extraño', // 或 'English'
  font: 'custom-msdf.json',
  'font-image': 'custom-msdf.png',
  negate: false,
  color: 'blue'
};

// 将 text 属性对象设置到实体
titleEntity.setAttribute('text', textProperties);

方案二:更新A-Frame到1.5.0或以上版本

A-Frame 在 1.5.0 版本开始提供了解决该问题的功能,更新到该版本即可自然修复该问题。使用 npm 命令即可将 a-frame 更新至最新的可用版本。但是更新框架可能引起与其他现有组件的兼容性问题,必须执行完整的回归测试。这个方式适合长期运行并积极迭代更新的大型项目。如果你的项目已停止更新,请谨慎选择这种方式来更新 A-Frame 的框架版本。
操作步骤:

  1. 使用 npm 或者 yarn 等包管理器更新 aframe 的版本到 1.5.0 或以上。

命令行指令:

npm install aframe@latest

或者

yarn upgrade aframe@latest
  1. 测试并处理潜在的组件兼容性问题

在项目的场景和所有页面中进行完整的场景构建和元素显示测试。若出现显示异常或者构建失败的问题,检查各组件的使用方法是否需要修改,或者尝试更新至最新的兼容版本。

安全建议

  • 确保字体文件 (custom-msdf.jsoncustom-msdf.png) 的来源可靠,避免使用未知或不安全的字体资源。
  • 字体文件存放路径的权限应合理配置,确保 A-Frame 场景能够正常访问这些文件。
  • 对字体渲染结果进行多设备和浏览器的兼容性测试,特别是目标平台的适配验证。

结语

通过上述方式设置 A-Frame 实体的 text 组件属性,就能解决自定义字体失效的问题。建议开发者们了解 A-Frame 的底层机制。使用属性对象方式设定 text 组件可以提高代码的可读性和可维护性,提升框架的处理效率。如有可能,保持A-frame的及时更新是解决问题的最好方式。这不仅能确保项目的正常开发和长期运维,也可以保障应用的构建过程更加安全高效,能够规避大多数因版本老旧出现的底层代码和依赖项上的潜在漏洞。