
xr-frame 是一套小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准,官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/xr-frame/overview/#%E6%A6%82%E8%BF%B0
这次我们产品有一个需求,要根据不同的卡片内容,展示对应的视频,并在画面中展示出来,这就用到了小程序的AR追踪器技术。非常非常不巧的是,我们的小程序用的框架是uniapp,而且还是vue3写的,所以你懂的,新技术就是不太会适配,好在查遍整个互联网,还是被我找到了解决办法,使用vue3+uniapp的时候很多人都会遇到各种奇奇怪怪的问题,我也是,这里就记录一下踩坑经历。演示项目已开源,开源地址:https://github.com/Sjj1024/uniapp-vue3
创建xr微信模块
在项目根目录wxcomponents中创建xr-2d-video组件,并创建小程序的文件:

文件包含:index.js + index.json + index.wxml三个
index.js内容如下:
javascript
Component({
behaviors: [require('../common/share-behavior').default],
properties: {
markerImg: {
type: String,
},
},
data: {
loaded: false,
arReady: false,
},
lifetimes: {
async attached() {
console.log('data', this.data)
},
},
methods: {
handleReady({ detail }) {
const xrScene = (this.scene = detail.value)
console.log('xr-scene', xrScene)
},
handleAssetsProgress: function ({ detail }) {
console.log('assets progress', detail.value)
},
handleAssetsLoaded: function ({ detail }) {
this.setData({ loaded: true })
},
handleTrackerSwitch: function (event) {
console.log('handleTrackerSwitch', event)
const detail = event.detail
const active = detail.value
const video = this.scene.assets.getAsset('video-texture', 'hikari')
active ? video.play() : video.stop()
},
handleTracker2Switch: function (event) {
console.log('handleTracker2Switch', event)
const detail = event.detail
const active = detail.value
const video = this.scene.assets.getAsset('video-texture', 'fire')
active ? video.play() : video.stop()
},
handleARReady: function ({ detail }) {
console.log('arReady')
this.setData({
arReady: true,
})
},
handleLog: function ({ detail }) {
console.log('log', detail.value)
},
},
})
index.json:
javascript
{
"component": true,
"usingComponents": {},
"renderer": "xr-frame"
}
index.wxml:
javascript
<xr-scene ar-system="modes:Marker" bind:ready="handleReady">
<xr-assets bind:loaded="handleAssetsLoaded">
<!-- 第一个视频 -->
<xr-asset-load
type="video-texture"
asset-id="hikari"
options="loop:true"
src="https://devusage.oss-cn-shanghai.aliyuncs.com/songjiangjiang/test/jian.mp4"
/>
<xr-asset-material
asset-id="mat"
effect="simple"
uniforms="u_baseColorMap: video-hikari"
/>
<!-- 第二个视频 -->
<xr-asset-load
type="video-texture"
asset-id="fire"
options="loop:true"
src="https://devusage.oss-cn-shanghai.aliyuncs.com/songjiangjiang/test/fire.mp4"
/>
<xr-asset-material
asset-id="mat2"
effect="simple"
uniforms="u_baseColorMap: video-fire"
/>
</xr-assets>
<xr-node wx:if="{{ loaded }}">
<xr-ar-tracker
mode="Marker"
bind:ar-tracker-switch="handleTrackerSwitch"
src="https://devusage.oss-cn-shanghai.aliyuncs.com/songjiangjiang/static/cat.jpg"
>
<xr-mesh node-id="mesh-plane" geometry="plane" material="mat" />
</xr-ar-tracker>
<xr-ar-tracker
mode="Marker"
bind:ar-tracker-switch="handleTracker2Switch"
src="https://devusage.oss-cn-shanghai.aliyuncs.com/songjiangjiang/test/yuji.jpg"
>
<xr-mesh node-id="mesh-plane2" geometry="plane" material="mat2" />
</xr-ar-tracker>
</xr-node>
<xr-camera clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>
在pages.json中添加这个组件:

Vue3中引入并使用
在vue3页面中创建并引入这个组件,注意要先获取到 screenWidth, screenHeight, renderWidth, renderHeight 这四个变量,分别是屏幕的宽高和渲染的宽高。通过uni.uni.getSystemInfo可以获取到设备的信息。

获取设备信息的代码:
javascript
// 获取顶部状态栏高度
uni.getSystemInfo({
success: (result: any) => {
// 获取手机系统的状态栏高度(不同手机的状态栏高度不同) ( 不要使用uni-app官方文档的var(--status-bar-height) 官方这个是固定的20px 不对的 )
console.log('当前手机设备信息', result)
let statusBarHeight = result.statusBarHeight + 'px'
// 获取右侧胶囊的信息 单位px
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
console.log('胶囊信息', menuButtonInfo)
//bottom: 胶囊底部距离屏幕顶部的距离
//height: 胶囊高度
//left: 胶囊左侧距离屏幕左侧的距离
//right: 胶囊右侧距离屏幕左侧的距离
//top: 胶囊顶部距离屏幕顶部的距离
//width: 胶囊宽度
// console.log(menuButtonInfo.width, menuButtonInfo.height, menuButtonInfo.top)
// console.log('计算胶囊右侧距离屏幕右边距离', result.screenWidth - menuButtonInfo.right)
let menuWidth = menuButtonInfo.width + 'px'
let menuHeight = menuButtonInfo.height + 'px'
let menuBorderRadius = menuButtonInfo.height / 2 + 'px'
let menuRight = result.screenWidth - menuButtonInfo.right + 'px'
let menuTop = menuButtonInfo.top + 'px'
let contentTop = result.statusBarHeight + 44 + 'px'
let menuLeft = menuButtonInfo.left + 'px'
let menuInfo = {
statusBarHeight: statusBarHeight, //状态栏高度----用来给自定义导航条页面的顶部导航条设计padding-top使用:目的留出系统的状态栏区域
menuWidth: menuWidth, //右侧的胶囊宽度--用来给自定义导航条页面的左侧胶囊设置使用
menuHeight: menuHeight, //右侧的胶囊高度--用来给自定义导航条页面的左侧胶囊设置使用
menuBorderRadius: menuBorderRadius, //一半的圆角--用来给自定义导航条页面的左侧胶囊设置使用
menuRight: menuRight, //右侧的胶囊距离右侧屏幕距离--用来给自定义导航条页面的左侧胶囊设置使用
menuTop: menuTop, //右侧的胶囊顶部距离屏幕顶部的距离--用来给自定义导航条页面的左侧胶囊设置使用
contentTop: contentTop,
screenHeight: result.screenHeight,
screenWidth: result.screenWidth,
devicePixelRatio: result.devicePixelRatio,
windowWidth: result.windowWidth, // 屏幕宽高,绘制海报时候使用
windowHeight: result.windowHeight,
menuLeft: menuLeft,
}
uni.setStorageSync('menuInfo', menuInfo)
},
fail: (error) => {
console.log(error)
},
})
还有最重要的,就是修改 node_modules/@dcloudio/uni-mp-weixin/dist/uni.compiler.js 这个文件中的一个变量:customElements,在里面添加上 xr-2d-video:

运行后,使用手机测试即可看到效果:
