# 組件
組件Component用于實(shí)現(xiàn)xr-frame中所有的邏輯,以生命周期拉驅(qū)動(dòng)。它們?cè)?code>wxml中對(duì)應(yīng)于每個(gè)標(biāo)簽上的屬性,比如<xr-element transform="position: 1 1 1;" />,就是在xr-element標(biāo)簽上掛在了一個(gè)transform組件。組件的聚合構(gòu)成了后面章節(jié)的元素,也就是wxml中對(duì)應(yīng)的標(biāo)簽。
組件的構(gòu)成主要分為兩部分——數(shù)據(jù)定義和生命周期。

# 讓我們定制一個(gè)組件
通過定制一個(gè)簡(jiǎn)單的組件,我們可以對(duì)組件的機(jī)制有比較清晰的了解:
// 這里只包括類型信息
import XrFrame from 'XrFrame';
// 這里是實(shí)例
const xrFrameSystem = wx.getXrFrameSystem();
// 自定義組件的數(shù)據(jù)接口
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`被掛載到場(chǎng)景時(shí)觸發(fā)的回調(diào)。
public onAdd(parent: XrFrame.Element, data: IAutoRotateData): void {
this._processData(data);
}
// 數(shù)據(jù)更新時(shí)觸發(fā)的回調(diào)。
public onUpdate(data: IAutoRotateData, preData: IAutoRotateData): void {
this._processData(data);
}
// 渲染每幀觸發(fā)的回調(diào)。
public onTick(delta: number, data: IAutoRotateData) {
const trs = this.el.getComponent(xrFrameSystem.Transform);
// 如果沒有掛載到一個(gè)`Node`節(jié)點(diǎn),則不生效。
if (!trs) {
return;
}
// 其實(shí)這里實(shí)現(xiàn)有點(diǎn)問題,不過是例子也無傷大雅了(逃
trs.rotation.x += this._speedX * 0.1;
trs.rotation.y += this._speedY * 0.1;
trs.rotation.z += this._speedZ * 0.1;
}
// 所掛載的`element`從父節(jié)點(diǎn)`parent`被移除時(shí),或者自己從`element`上唄移除時(shí),觸發(fā)的回調(diào)。
// 一般用于消除功能的運(yùn)作。
public onRemove(parent: XrFrame.Element, data: IAutoRotateData): void {
}
// 從被掛載的`element`上被移除,或是`element`被銷毀時(shí),觸發(fā)的回調(diào)。
// 一般用于釋放持有的資源。
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;
}
}
// 最后將組件注冊(cè)進(jìn)框架,名為`auto-rotate`
xrFrameSystem.registerComponent('auto-rotate', AutoRotate);
通過以上代碼,我們定制了一個(gè)為掛載的元素提供旋轉(zhuǎn)功能的組件。
首先可以看到的是IAutoRotateData和AutoRotateSchema,接口是為了通過泛型給定義的組件提供類型推斷,而真正作用于運(yùn)行時(shí)邏輯的,則是那個(gè)schema,其類型定義為:
interface IComponentSchema {
[key: string]: {type: string, defaultValue?: any}
}
在schema中,我們定義了組件每個(gè)數(shù)據(jù)的類型type和默認(rèn)值defaultValue。這是由于所有組件在xml中對(duì)應(yīng)的屬性的值都是字符串,所以需要告訴框架如何將這些字符串轉(zhuǎn)換成最終的值,這一部分詳見組件數(shù)據(jù)解析。
在數(shù)據(jù)定義后,我們繼承了xrFrameSystem.Component派生了一個(gè)子類,它有一些生命周期,這就是組件具體邏輯實(shí)現(xiàn)的載體。我們?cè)?code>onAdd和onUpdate的時(shí)候去更新旋轉(zhuǎn)速度數(shù)據(jù),然后在每幀onTick的時(shí)候去修改掛載元素的transform組件中的旋轉(zhuǎn)。注意還有onRemove和onRelease兩個(gè)生命周期,但由于本組件沒有額外資源需要處理,所以不需要執(zhí)行任何操作。
完成了邏輯的實(shí)現(xiàn)后,我們最后一步調(diào)用了xrFrameSystem.registerComponent將這個(gè)組件注冊(cè)到了框架中,并給它了一個(gè)名字auto-rotate,接下來就可以正常使用了。
# 使用這個(gè)組件
使用組件的方式有兩種:
# 在wxml中使用
首先是在wxml中使用,我們只需要按照注冊(cè)的名字和數(shù)據(jù)類型定義將其寫在元素標(biāo)簽上即可:
<xr-node auto-rotate="speed: 2 3 1;" />
如此這個(gè)節(jié)點(diǎn)便會(huì)自動(dòng)旋轉(zhuǎn),速度為{x: 2, y: 3, z: 1}。
組件還可以通過代理的方式來簡(jiǎn)化使用,詳見元素中的數(shù)據(jù)代理部分。
# 手動(dòng)使用
我們也可以在運(yùn)行時(shí)來手動(dòng)添加組件或設(shè)置它的數(shù)據(jù),但注意一定不要和xml中沖突!
要手動(dòng)使用組件,我們需要先拿到元素的實(shí)例引用,詳見場(chǎng)景中獲取元素引用相關(guān)的描述,拿到了引用后便可以執(zhí)行操作:
// 添加組件,并給一個(gè)可選的初始值
el.addComponent(AutoRotate, {speed: [2, 3, 1]});
// 獲取組件并更新數(shù)據(jù),注意`setData`包括`getData`都是通用方法,但有些組件有自己的實(shí)現(xiàn),比如`transform.position`。
el.getComponent(AutoRotate).setData({speed: [1, 1, 1]});
// 手動(dòng)移除組件
el.removeComponent(AutoRotate);