组件数据解析

在组件一章我们知道需要有一个解析器将xml中组件对应属性的字符串转换为组件需要的数据类型,对此,微信小程序框架提供了一套机制来处理。

数据解析器

数据解析器IDataValueHandler就是用来将属性字符串转换成特定类型的数据的,其具体定义如下:

interface IDataValueHandler<TDataValue> {
  create(value: string, defaultValue: any, scene: Scene): TDataValue;
}

可见主要是一个create方法,其接受一个字符串的value,一个组件schema中定义的默认值defaultValue和场景引用scene,返回解析后的值。一般我们会如此注册一个解析器:

registerDataValue('number', {create: (value: string, defaultValue: any, scene: Scene) => {
  return value === undefined ? defaultValue : parseFloat(value));
}});

这里注册了number类型的解析器,可以看到如果没有传入值则返回默认值,否则用parseFloat转换。

除了registerDataValue方式注册的数据类型外,还有一类特殊类型的数据,它们就是资源

特殊类型-资源

资源数据和number这样的普通数据有些不同,其注册一般不使用registerDataValue,而是在registerAssetLoader时定义的。如何定义可以参考资源加载器的内容,一般来讲资源数据使用时填写的值都是资源ID,比如你要使用纹理资源:

<xr-asset-load type="texture" asset-id="waifu" src="/assets/textures/waifu.jpg" />
<xr-asset-material asset-id="simple-mat" uniforms="u_baseColorMap: waifu" />

我们先加载了一张id为waifu的纹理,然后在下面材质的uniform的u_baseColorMap属性使用了它。

资源类型的数据在schema中描述时,默认值是资源的id 注意资源类型的数据还会影响到组件的onAddonUpdate生命周期,微信小程序框架会在每一次资源数据更新后,先等待引用的资源加载完成,才会进入这两个生命周期

内置数据类型

微信小程序框架内置了一些数据类型:

非资源数据

类型 例子 转换 说明
string henshin: KuugaAgitoRyuki FaziBladeHibikiKabutoDenOkiva DecadeWOOOFourzeWizardGaim DriveGhostExaidbuildGrandZio ‘KuugaAgitoRyuki FaziBladeHibikiKabutoDenOkiva DecadeWOOOFourzeWizardGaim DriveGhostExaidbuildGrandZio’ 字符串,不作任何处理
number truth:42 42 数字,会转成float,支持其他进制如0xff0b11
boolean yiyandingzhen:false false 布尔,当写’false’时为false,否则均为true(包括不写值)
array producer:wowaka neru kurogaki [‘wowaka’,’neru’,’kurogaki’] 字符串数组,用空格分割
number-array idolOffice:7 6 5 [7,6,5] 数字数组,用空格分割
color rem:0.57 0.75 1 1 [0.57,0.75,1,1] 颜色,rgba,也可以使用#fff这种方式
map cat:10,dog:8,fox:6 [[‘cat’,10],[‘dog’,8],[‘fox’,6]] 映射形式是key:value,用,分割
dict camp:瞬光:混乱善良,roam:中立善良,xinyi:绝对中立 {瞬光:’混乱善良’,roam:’中立善良’,xinyi:’绝对中立’} 字典,和map类型写起来一样,转换结果不同
transform target:homo nodeId为homo的Transform组件引用 变换,可以用于索引标记过nodeId的变换组件

资源数据

资源数据可见内置的各种资源:效果,图片,纹理,材质,几何数据,模型,渲染目标,帧动画。

组件

组件Component用于实现xr-frame中所有的逻辑,以生命周期拉驱动。它们在wxml中对应于每个标签上的属性,比如<xr-element transform="position: 1 1 1;" />,就是在xr-element标签上挂在了一个transform组件。组件的聚合构成了后面章节的元素,也就是wxml中对应的标签。

组件的构成主要分为两部分——数据定义和生命周期。

让我们定制一个组件

通过定制一个简单的组件,我们可以对组件的机制有比较清晰的了解:

// 这里只包括类型信息
import XrFrame from 'XrFrame';
// 这里是实例
const xrFrameSystem = wx.getXrFrameSystem();

// 自定义组件的数据接口
export interface IAutoRotateData {
  speed: number[];
}

// 自定义组件的`schema`,详见后面论述
const AutoRotateSchema: XrFrame.IComponentSchema = {
  speed: {type: 'number-array', defaultValue: [1, 1, 1]}
}

// 定义组件
class AutoRotate extends xrFrameSystem.Component<IAutoRotateData> {
  public readonly schema: XrFrame.IComponentSchema = AutoRotateSchema;

  private _speedX: number = 1;
  private _speedY: number = 1;
  private _speedZ: number = 1;

  // 所挂载的`element`被挂载到场景时触发的回调。
  public onAdd(parent: XrFrame.Element, data: IAutoRotateData): void {
    this._processData(data);
  }

  // 数据更新时触发的回调。
  public onUpdate(data: IAutoRotateData, preData: IAutoRotateData): void {
    this._processData(data);
  }

  // 渲染每帧触发的回调。
  public onTick(delta: number, data: IAutoRotateData) {
    const trs = this.el.getComponent(xrFrameSystem.Transform);

    // 如果没有挂载到一个`Node`节点,则不生效。
    if (!trs) {
      return;
    }

    // 其实这里实现有点问题,不过是例子也无伤大雅了(逃
    trs.rotation.x += this._speedX * 0.1;
    trs.rotation.y += this._speedY * 0.1;
    trs.rotation.z += this._speedZ * 0.1;
  }

  // 所挂载的`element`从父节点`parent`被移除时,或者自己从`element`上呗移除时,触发的回调。
  // 一般用于消除功能的运作。
  public onRemove(parent: XrFrame.Element, data: IAutoRotateData): void {

  }

  // 从被挂载的`element`上被移除,或是`element`被销毁时,触发的回调。
  // 一般用于释放持有的资源。
  public onRelease(data: IAutoRotateData) {

  }

  private _processData(data: IAutoRotateData) {
    this._speedX = data.speed?.[0] !== undefined ? data.speed[0] : 1;
    this._speedY = data.speed?.[1] !== undefined ? data.speed[1] : 1;
    this._speedZ = data.speed?.[2] !== undefined ? data.speed[2] : 1;
  }
}

// 最后将组件注册进框架,名为`auto-rotate`
xrFrameSystem.registerComponent('auto-rotate', AutoRotate);

通过以上代码,我们定制了一个为挂载的元素提供旋转功能的组件。

首先可以看到的是IAutoRotateDataAutoRotateSchema,接口是为了通过泛型给定义的组件提供类型推断,而真正作用于运行时逻辑的,则是那个schema,其类型定义为:

interface IComponentSchema {
  [key: string]: {type: string, defaultValue?: any}
}

schema中,我们定义了组件每个数据的类型type和默认值defaultValue。这是由于所有组件在xml中对应的属性的值都是字符串,所以需要告诉微信小程序框架如何将这些字符串转换成最终的值,这一部分详见组件数据解析。

在数据定义后,我们继承了xrFrameSystem.Component派生了一个子类,它有一些生命周期,这就是组件具体逻辑实现的载体。我们在onAddonUpdate的时候去更新旋转速度数据,然后在每帧onTick的时候去修改挂载元素的transform组件中的旋转。注意还有onRemoveonRelease两个生命周期,但由于本组件没有额外资源需要处理,所以不需要执行任何操作。

完成了逻辑的实现后,我们最后一步调用了xrFrameSystem.registerComponent将这个组件注册到了微信小程序框架中,并给它了一个名字auto-rotate,接下来就可以正常使用了。

使用这个组件

使用组件的方式有两种:

在wxml中使用

首先是在wxml中使用,我们只需要按照注册的名字和数据类型定义将其写在元素标签上即可:

<xr-node auto-rotate="speed: 2 3 1;" />

如此这个节点便会自动旋转,速度为{x: 2, y: 3, z: 1}

组件还可以通过代理的方式来简化使用,详见元素中的数据代理部分。

手动使用

我们也可以在运行时来手动添加组件或设置它的数据,但注意一定不要和xml中冲突

要手动使用组件,我们需要先拿到元素的实例引用,详见场景中获取元素引用相关的描述,拿到了引用后便可以执行操作:

// 添加组件,并给一个可选的初始值
el.addComponent(AutoRotate, {speed: [2, 3, 1]});

// 获取组件并更新数据,注意`setData`包括`getData`都是通用方法,但有些组件有自己的实现,比如`transform.position`。
el.getComponent(AutoRotate).setData({speed: [1, 1, 1]});

// 手动移除组件
el.removeComponent(AutoRotate);

架构设计

xr-frame使用的架构属于ECS的一种,对应于xml中,Entity就是元素,Component就是属性,System就是场景的顶层组件。这样的设计在严谨的范式下,给开发者提供了灵活的扩展能力,理论上所有的内置功能都可以由开发者在外部自己实现。

ECS

开发者在xml中写的标签,都对应于框架里的元素Element的子类。元素本身不包含逻辑,它的逻辑全部都是被代理到其拥有的组件Component实现的。

而这个组件,在xml中对应的是元素上的属性,比如<xr-element transform="position: 1 1 1;" />,就是说在名为Element的元素上挂载了名为transform的组件。当然为了简化使用,我们还提供了一种属性代理的策略,详见Element的文档。

注意,虽然开发者会发现我们提供了手动给元素添加子节点的功能,但大部分情况下这是非法的!!!当然,出于某些场景的性能和灵活性考虑,我们提供了Shadow Element来满足开发者的需求。 在所有元素的子类中,有一个是最为特殊的,它就是场景Scene,对应于xml中的xr-scene标签。设计上来讲,每个xr-frame微信小程序组件顶层都必须是一个xr-scene元素,其他的元素都必须被包含在其中。这是因为场景负责整个框架的调度、各个系统的挂载以及所有资源的管理。

系统,就是挂载于场景下的各个顶层组件,它们负责每个模块的管理,按照顺序驱动各个模块的运行。目前而言,所有的内置功能由以下几个系统和对应的组件们构成。

逻辑系统

逻辑系统负责驱动所有元素以及组件的生命周期,保证它们按顺序进行,开发者一般只需要关心组件的生命周期即可,详见Component。

节点系统

节点系统负责管理场景中所有3D节点的可见性,详见Node。

渲染系统

渲染系统负责整个场景的渲染,也是最复杂的一个系统,它将Geometry、Effect、Material、Mesh、Texture、Light和Camera等组织起来,送入管线进行渲染。

渲染管线从设计上是可定制的,未来将会逐步开放给开发者。

资源系统

资源系统管理整个场景的资源(注意资源是每个场景专属的),一般通过AssetsAssetsLoad组件以及对应的元素在xml中使用,也提供手动管理的接口。同时还支持开发者自定义Loader来增加新的资源类型。

动画系统

动画系统管理所有的动画,其主要实现是借由每个元素的Animator组件来管理下面的所有AnimationAnimation本身被设计成可扩展的,框架内置了两种Animation来方便开发者简单使用:帧动画Keyframe和gltf动画。

物理系统

物理系统目前仅管理场景中的轮廓,以及触发轮廓点击事件。未来计划加入真实物理效果模拟,包括碰撞体和刚体等。

AR系统

AR系统将xr-frame微信小程序AI系统关联起来,让AR变得十分简单易用。主系统和ARTracker组件协作,让开发者用几行xml就能实现平面跟踪、2D/3DMarker识别等功能。

限制和展望

限制:

  1. 最低要求客户端iOS8.0.29、安卓8.0.30及以上,推荐稳定版在iOS8.0.36、安卓8.0.35及以上。
  2. 基础库最低2.27.1及以上,推荐2.32.0及以上。
  3. 开发工具需要最新版本,建议Nightly版本
  4. 微信小程序全局同一时刻只能存在一个xr-frame组件,否则可能会发生异常。
  5. 同一个xr-frame组件只能存在一个xr-scene,并且必须为顶层。
  6. 目前不支持和微信小程序传统标签比如<view>混写。
  7. 目前不支持wxml自动补全,真机调试需要特别注意,见真机调试文档。

同时未来还会追加更多的能力,在未来的规划中,我们还会着重致力于:

  1. XR-FRAME内置特色的UI组件,让开发者可以在XR-FRAME组件中写UI,来实现一套酷炫的UI系统。
  2. AR/VR能力持续增强,支持眼睛设备。
  3. 交互手段进一步强化,物理碰撞、触发等功能(已完成,待发布)。
  4. 工具能力强化,包括标签属性自动补全等。

想要进一步了解整个框架的架构,请看架构一节。

示例

上面的例子只是一部分简略代码,我们提供了丰富的示例并开源在xr-frame-demo,可以直接扫码体验:

demo

以下是部分示例需要用到的扫描资源:

典型案例 -> 扫描图片视频

典型案例 -> 扫描透视模型

典型案例 -> 扫描微信球

AR案例 -> 2DMarker

AR案例 -> OSD Marker

特性

比起目前的Canvas组件,xr-frame有以下显著的优势:

🍰 高度整合,上手简单

提供xml的方式来描述3D场景,并集成了AR、物理、动画、粒子、后处理等等系统,上手简单,符合微信小程序开发规范。

🌈 渲染效果好

内置完整的PBR效果、环境光照、阴影,原生支持glTF资源,提供的xr-frame-toolkit可以快速通过全景图生成环境数据。

⚡️ 高性能

混合方案,渲染性能逼近原生,xr-frame-toolkit可以对外部glTF文件进行优化,来提高加载性能,还有完善的缓存机制保证二次进入的加载性能。

🧱 易扩展

扩展性强,从资源到组件元素,皆可以由高阶用户自己定制,未来还可以暴露内部的渲染管线定制能力。

概述

本文适合有初步了解的开发者阅读,入门请参见指南

⚠️ xr-frame在基础库v2.32.0开始基本稳定,发布为正式版,但仍有一些功能还在开发,请见限制和展望。

xr-frame是一套微信小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循微信小程序开发标准:

<xr-scene>
  <xr-assets>
    <!-- 加载一个GLTF模型 -->
    <xr-asset-load type="gltf" asset-id="gltf-model" src="..." />
  </xr-assets>
  <xr-env env-data="..." />

  <xr-node>
    <!-- 将一个GLTF模型渲染在AR场景中 -->
    <xr-ar-tracker ...>
      <xr-gltf model="gltf-model" ...></xr-gltf>
    </xr-ar-tracker>
    <xr-camera is-ar-camera ...></xr-camera>
  </xr-node>

  <xr-node>
    <!-- 场景光照 -->
    <xr-light type="ambient" ... />
    <xr-light type="directional" ... />
  </xr-node>
</xr-scene>

特性

比起目前的Canvas组件,xr-frame有以下显著的优势:

🍰 高度整合,上手简单

提供xml的方式来描述3D场景,并集成了AR、物理、动画、粒子、后处理等等系统,上手简单,符合微信小程序开发规范。

🌈 渲染效果好

内置完整的PBR效果、环境光照、阴影,原生支持glTF资源,提供的xr-frame-toolkit可以快速通过全景图生成环境数据。

⚡️ 高性能

混合方案,渲染性能逼近原生,xr-frame-toolkit可以对外部glTF文件进行优化,来提高加载性能,还有完善的缓存机制保证二次进入的加载性能。

🧱 易扩展

扩展性强,从资源到组件元素,皆可以由高阶用户自己定制,未来还可以暴露内部的渲染管线定制能力。

示例

上面的例子只是一部分简略代码,我们提供了丰富的示例并开源在xr-frame-demo,可以直接扫码体验:

demo

以下是部分示例需要用到的扫描资源:

典型案例 -> 扫描图片视频

典型案例 -> 扫描透视模型

典型案例 -> 扫描微信球

AR案例 -> 2DMarker

AR案例 -> OSD Marker

限制和展望

限制:

  1. 最低要求客户端iOS8.0.29、安卓8.0.30及以上,推荐稳定版在iOS8.0.36、安卓8.0.35及以上。
  2. 基础库最低2.27.1及以上,推荐2.32.0及以上。
  3. 开发工具需要最新版本,建议Nightly版本
  4. 微信小程序全局同一时刻只能存在一个xr-frame组件,否则可能会发生异常。
  5. 同一个xr-frame组件只能存在一个xr-scene,并且必须为顶层。
  6. 目前不支持和微信小程序传统标签比如<view>混写。
  7. 目前不支持wxml自动补全,真机调试需要特别注意,见真机调试文档。

同时未来还会追加更多的能力,在未来的规划中,我们还会着重致力于:

  1. XR-FRAME内置特色的UI组件,让开发者可以在XR-FRAME组件中写UI,来实现一套酷炫的UI系统。
  2. AR/VR能力持续增强,支持眼睛设备。
  3. 交互手段进一步强化,物理碰撞、触发等功能(已完成,待发布)。
  4. 工具能力强化,包括标签属性自动补全等。

想要进一步了解整个框架的架构,请看架构一节。

page-meta

基础库 2.9.0 开始支持,低版本需做兼容处理。

微信 Windows 版:支持

微信 Mac 版:支持

微信 鸿蒙 OS 版:支持

渲染框架支持情况:Skyline (使用最新 Nightly 工具调试)、WebView

功能描述

页面属性配置节点,用于指定页面的一些属性、监听页面事件。只能是页面内的第一个节点。可以配合 navigation-bar 组件一同使用。

通过这个节点可以获得类似于调用 wx.setBackgroundTextStyle wx.setBackgroundColor 等接口调用的效果。

通用属性

属性 类型 默认值 必填 说明 最低版本
background-text-style string 下拉背景字体、loading 图的样式,仅支持 darklight 2.9.0
background-color string 窗口的背景色,必须为十六进制颜色值 2.9.0
background-color-top string 顶部窗口的背景色,必须为十六进制颜色值,仅 iOS 支持 2.9.0
background-color-bottom string 底部窗口的背景色,必须为十六进制颜色值,仅 iOS 支持 2.9.0
root-background-color string 页面内容的背景色,用于页面中的空白部分和页面大小变化 resize 动画期间的临时空闲区域 2.12.1
page-style string “” 页面根节点样式,页面根节点是所有页面节点的祖先节点,相当于 HTML 中的 body 节点 2.9.0
page-font-size string “” 页面 page 的字体大小,可以设置为 system ,表示使用当前用户设置的微信字体大小 2.11.0
root-font-size string “” 页面的根字体大小,页面中的所有 rem 单位,将使用这个字体大小作为参考值,即 1rem 等于这个字体大小;自微信小程序版本 2.11.0 起,也可以设置为 system 2.9.0
page-orientation string “” 页面的方向,可为 auto portraitlandscape 2.12.0
bindresize eventhandle 页面尺寸变化时会触发 resize 事件, event.detail = { size: { windowWidth, windowHeight } } 2.9.0

WebView 特有属性

属性 类型 默认值 必填 说明 最低版本
scroll-top string “” 滚动位置,可以使用 px 或者 rpx 为单位,在被设置时,页面会滚动到对应位置 2.9.0
scroll-duration number 300 滚动动画时长 2.9.0
bindscroll eventhandle 页面滚动时会触发 scroll 事件, event.detail = { scrollTop } 2.9.0
bindscrolldone eventhandle 如果通过改变 scroll-top 属性来使页面滚动,页面滚动结束后会触发 scrolldone 事件 2.9.0

示例代码

在开发者工具中预览效果

<page-meta
  background-text-style="{{bgTextStyle}}"
  background-color="{{bgColor}}"
  background-color-top="{{bgColorTop}}"
  background-color-bottom="{{bgColorBottom}}"
  scroll-top="{{scrollTop}}"
  page-style="color: green"
  root-font-size="16px"
>
  <navigation-bar
    title="{{nbTitle}}"
    loading="{{nbLoading}}"
    front-color="{{nbFrontColor}}"
    background-color="{{nbBackgroundColor}}"
  />
</page-meta>
Page({
  data: {
    bgTextStyle: 'dark',
    scrollTop: '200rpx',
    bgColor: '#ff0000',
    bgColorTop: '#00ff00',
    bgColorBottom: '#0000ff',
    nbTitle: '标题',
    nbLoading: false,
    nbFrontColor: '#000000',
    nbBackgroundColor: '#ffffff',
  },
})

navigation-bar

基础库 2.9.0 开始支持,低版本需做兼容处理。

微信 Windows 版:支持

微信 Mac 版:支持

微信 鸿蒙 OS 版:支持

渲染框架支持情况:WebView

功能描述

页面导航条配置节点,用于指定导航栏的一些属性。只能是 page-meta 组件内的第一个节点,需要配合它一同使用。

通过这个节点可以获得类似于调用 wx.setNavigationBarTitle wx.setNavigationBarColor 等接口调用的效果。

属性说明

属性 类型 默认值 必填 说明 最低版本
title string 导航条标题 2.9.0
loading boolean false 是否在导航条显示 loading 加载提示 2.9.0
front-color string 导航条前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff#000000 2.9.0
background-color string 导航条背景颜色值,有效值为十六进制颜色 2.9.0
color-animation-duration number 0 改变导航栏颜色时的动画时长,默认为 (即没有动画效果) 2.9.0
color-animation-timing-func string “linear” 改变导航栏颜色时的动画方式,支持 lineareaseIneaseOuteaseInOut 2.9.0

示例代码

<page-meta>
  <navigation-bar
    title="{{nbTitle}}"
    loading="{{nbLoading}}"
    front-color="{{nbFrontColor}}"
    background-color="{{nbBackgroundColor}}"
    color-animation-duration="2000"
    color-animation-timing-func="easeIn"
  />
</page-meta>
Page({
  data: {
    nbFrontColor: '#000000',
    nbBackgroundColor: '#ffffff',
  },
  onLoad() {
    this.setData({
      nbTitle: '新标题',
      nbLoading: true,
      nbFrontColor: '#ffffff',
      nbBackgroundColor: '#000000',
    })
  }
})

aria-component

微信 Windows 版:支持

微信 Mac 版:支持

微信 鸿蒙 OS 版:支持

渲染框架支持情况:Skyline (使用最新 Nightly 工具调试)、WebView

功能描述

满足视障人士对于微信小程序的访问需求。

无障碍访问

为了更好地满足视障人士对于微信小程序的访问需求,基础库自2.7.1起,支持部分ARIA标签。

无障碍特性在读屏模式下可以访问,iOS可通过设置->通用->辅助功能->旁白打开。

以 view 组件为例,开发者可以增加aria-rolearia-label属性。 其中aria-role表示组件的角色,当设置为’img’时,读屏模式下聚焦后系统会朗读出’图像’。设置为’button’时,聚焦后后系统朗读出’按钮’。aria-label表示组件附带的额外信息,聚焦后系统会自动朗读出来。

微信小程序已经内置了一些无障碍的特性,对于非原生组件,开发者可以添加以下无障碍标签。

aria-activedescendant aria-atomic aria-autocomplete aria-busy aria-checked
aria-colcount aria-colindex aria-colspan aria-controls aria-current
aria-describedby aria-details aria-disabled aria-dropeffect aria-errormessage
aria-expanded aria-flowto aria-grabbed aria-haspopup aria-hidden
aria-invalid aria-keyshortcuts aria-label aria-labelledby aria-level
aria-live aria-modal aria-multiline aria-multiselectable aria-orientation
aria-owns aria-placeholder aria-posinset aria-pressed aria-readonly
aria-relevant aria-required aria-role aria-roledescription aria-rowcount
aria-rowindex aria-rowspan aria-selected aria-setsize aria-sort
aria-valuemax aria-valuemin aria-valuenow aria-valuetext

示例代码

<view aria-role="button" aria-label="提交表单">提交</view>

Tips

  1. 安卓和iOS读屏模式下设置aria-role后朗读的内容不同系统之间会有差异
  2. 可设置的aria-role可参看 Using Aria中的Widget Roles,部分role的设置在移动端可能无效。