在使用xr-frame開發3D小程序時,我們經常需要根據需求去動態加載模型或刪除模型,在官方的說明中,提到了相關方法,但并不太明確,也沒有確切的實例。
我們先來看一下官方給出的說明。
一. Shadow元素
我們需要用代碼動態創建元素之后添加到場景中,這個需求和wxml
寫標簽這種靜態的模板編譯方式是沖突的,為了保證DOM樹不混亂,我們提供了類似于HTML中的ShadowRoot
的XRShadow元素,對應于xml
中的xr-shadow
標簽,來解決這個問題。
<xr-shadow id="shadow" position="0 1 0" />
也就是說,我們要動態添加模型,就需要在這個xr-shadow元素下操作。
我們先來看一下實例

實例中,我們通過點擊下一個按鈕,將xr-frame框架中的模型卸載后再重新加載一個新的模型。
二.代碼實現
下面我們從源碼角度來講解如何實現。
(1)index.wxml?中的部分代碼
<xr-scene render-system="alpha:true" bind:ready="handleReady"><xr-light type="ambient" color="1 1 1" intensity="1" /><xr-light type="directional" rotation="40 150 0" color="1 1 1" intensity="3" cast-shadow /><xr-node node-id="target" anim-keyframe="anim" anim-autoplay="clip:cube"><xr-shadow id="shadow" position="0 0 0" /></xr-node><xr-env env-data="xr-frame-team-workspace-day" sky-map="weakme"/><xr-camera id="camera" clear-color="0 0 0 0" position="0 0.5 1.5" target="target" camera-orbit-control background="skybox"/>
</xr-scene>
上面的代碼中,我們主要看
<xr-shadow id="shadow" position="0 0 0" />
這段源碼就是shadow元素,我們要添加和刪除模型,都是在這個元素里操作的。
我們給shadow元素設置了id,就是用于在 index.js 文件中查找這個元素,然后通過xr-frame框架下的API操作在這個元素下添加新的節點。
(2)獲取場景中的shadow節點
在?index.js?中的源碼中
handleReady({detail}) {this.xrFrameSystem = wx.getXrFrameSystem()this.scene = detail.valuethis.control.isLockZoom = truethis.shadow = this.scene.getElementById('shadow')// 給父組件發送通知this.triggerEvent('sceneReady',"ready")},
handleReady() 這個函數表示場景加載完成后執行的回調函數。
在這個函數中,我們通過?this.scene = detail.value?獲取到了場景的根節點信息,這一步是基礎,獲取到場景的根節點后,才能獲取更多的子節點。
隨后,我們通過?this.shadow =this.scene.getElementById('shadow')?獲取到了?shadow?元素的節點,下一步,我們就要實現在?shadow?元素下添加子節點的操作了。
(3)使用api動態創建gltf節點到shadow中。
async addGltf() {//1 創建xr-gltf元素const el = this.scene.createElement(this.xrFrameSystem.XRGLTF)//2 獲取shadow節點this.shadow = this.scene.getElementById('shadow')//3 將xr-gltf節點加入到shaodow節點中this.shadow.addChild(el)//4 讀取對應的模型文件const { value: model} = await this.scene.assets.loadAsset({type: 'gltf',assetId: 'gltfmodel',src: modelsrc})// 5 獲取gltf組件this.gltfComp = el.getComponent('gltf') // 6 給gltf組件設置模型數據this.gltfComp.setData({model: model})// 7 給元素設置一個ID,用于后續方便removeel.setId(name)
}
注意:這個函數前面加了 async ,是一個異步函數。
因為在函數中我們需要做加載模型文件的操作,這個操作可能會很耗時,需要視網絡情況和模型大小而定。
下面我們一步步解析:
..1 源碼中,我們先創建了一個xr-gltf元素
const el = this.scene.createElement(this.xrFrameSystem.XRGLTF)
????????這里通過框架的API createElement()我們創建了一個元素
..2?然后將該元素作為子元素添加到shadow元素中
//2 獲取shadow節點
this.shadow = this.scene.getElementById('shadow')
//3 將xr-gltf節點加入到shaodow節點中
this.shadow.addChild(el)
? ? ? ? 這里我們先通過 getElementById() 方法找到shadow節點。
? ? ? ? 然后,通過?addChild() 方法將創建的XRGLTF元素添加到節點下。
..3?通過?scene.assets.loadAsset()?方法加載一個模型資源
const { value: model} = await this.scene.assets.loadAsset({type: 'gltf',assetId: 'gltfmodel',src: modelsrc})
這個方法中有幾個屬性:
type?表示要加載的資源類型,這里設置為gltf。
assetId?表示設置的資源ID,這個可以用于后期清理資源。
src?表示資源的網絡地址。
這里需要注意的一點,scene.assets.loadAsset()?這個方法要加載資源,有時耗費時間會比較長,所以這里采用async/await?的語法來進行異步操作。
..4?獲取到xr-gltf元素中的 gltf 組件
this.gltfComp = el.getComponent('gltf')
這一部,我們獲取到了元素XRGLTF元素中的gltf組件
..5 給組件設置屬性
// 6 給gltf組件設置模型數據
this.gltfComp.setData({model: model})
// 7 給元素設置一個ID,用于后續方便remove
el.setId(name)
這一步我們給組件中的model屬性設置了變量名為?model?的對象
這個 model 就是我們第3步加載的模型對象
最后我們給元素 el 設置了一個id。用于后期清理資源使用。
(4)清理資源
在官方說明中,通過動態加載的資源需要自己手動釋放,不然如果加載越來越多,會對程序造成卡頓。
release(name) {//1 刪除節點this.shadow.removeChild(this.scene.getElementById(name))//2 釋放資源this.scene.assets.releaseAsset('gltf', name)
},
上面代碼中,我們首先調用了 shadow.removeChild() 方法,移除了shadow下的子節點。
然后通過 scene.assets.releaseAsset()方法,釋放了加載的gltf資源。
函數中的參數 name 就是我們給上一步創建的 el 設置的 id
函數中的這兩個方法都是xr-frame官方api中的方法,詳細的解釋可以看一下官方文件。
關于釋放資源的時機,可以根據自己的場景需求進行合理的設計。
三.附件說明
附件中的代碼就是本篇文章用到的動態加載模型的xr-frame組件中的代碼。
代碼中我做了一些優化,由于gltf文件有時會過大,所以我采用模型和貼圖分開加載的方法,加載完成后再給模型設置上貼圖。
四.示例小程序
附件中的小程序碼,是我根據官方指南做的一個簡易的示例程序,其中運用了場景的搭建,材質動態修改,模型動態添加與刪除,粒子特效等功能,可以作為本片文章的參考。