在使用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文件有时会过大,所以我采用模型和贴图分开加载的方法,加载完成后再给模型设置上贴图。
四.示例小程序
附件中的小程序码,是我根据官方指南做的一个简易的示例程序,其中运用了场景的搭建,材质动态修改,模型动态添加与删除,粒子特效等功能,可以作为本片文章的参考。