本人这边项目里有个需求,就是人脸检测后,将指定的模型附加到使用者的脸上,随后有一个拍摄照片功能,将附加模型后的用户的画面截取出来,并存储到手机本地中,现在人脸识别,以及相关模型也附加到人脸上,但是在拍摄上出现问题,总是保存失败,wxml文件:
<xr-scene ar-system="modes:Face;camera:Front" bind:ready="handleReady" bind:tick="handleTick">
<xr-assets bind:loaded="handleAssetsLoaded">
<xr-asset-load type="gltf" asset-id="mask" src="{{ObjectPath}}" />
</xr-assets>
<xr-env env-data="xr-frame-team-workspace-day" />
<xr-light type="ambient" color="1 1 1" intensity="1" />
<xr-light type="directional" rotation="40 70 0" color="1 1 1" intensity="3" />
<xr-ar-tracker mode="Face" auto-sync="43">
<xr-gltf id="mask" model="mask" rotation="0 180 0" scale="0.5 0.5 0.5" />
</xr-ar-tracker>
<xr-camera id="camera" clear-color="0.4 0.8 0.6 1" background="ar" is-ar-camera />
</xr-scene>
ts 文件:
import { MarkerPath, SceneState } from "../../utils/data";
// components/user-xr-start/index.ts
Component({
/**
* 组件的属性列表
*/
properties: {
modelPath:{
type:String,
value:""
}
},
/**
* 组件的初始数据
*/
data: {
loaded:false,
hasFrontCamera: false,
cameraPermissionGranted: false,
ObjectPath:MarkerPath.object,
tempImagePath:"",
currentState:SceneState.Init,
},
// 添加生命周期函数
lifetimes: {
attached: function() {
// 初始化实例属性
this.scene = null;
this.camera = null;
this.mask = null;
this.tmpV3 = null;
// 组件加载时检查相机状态
this.checkCameraStatus();
},
detached: function() {
// 清理引用
this.scene = null;
this.camera = null;
this.mask = null;
this.tmpV3 = null;
}
},
scene: null,
mask: null,
/**
* 组件的方法列表
*/
methods: {
// 检查相机状态
checkCameraStatus: function() {
const that = this;
// 1. 检查是否有前置相机
wx.getSystemInfo({
success: function(res) {
// 大多数现代手机都有前置相机,但可以进一步确认
that.setData({
hasFrontCamera: true // 假设有前置相机
});
// 2. 检查相机权限
that.checkCameraPermission();
},
fail: function(err) {
console.error('获取系统信息失败:', err);
that.showCameraWarning('无法检测相机硬件状态');
}
});
},
// 检查相机权限
checkCameraPermission: function() {
const that = this;
wx.getSetting({
success: (res) => {
if (res.authSetting['scope.camera']) {
// 已授权相机权限
that.setData({
cameraPermissionGranted: true
});
} else {
// 未授权相机权限
that.setData({
cameraPermissionGranted: false
});
that.requestCameraPermission();
}
},
fail: (err) => {
console.error('获取设置失败:', err);
that.showCameraWarning('无法检测相机权限状态');
}
});
},
// 请求相机权限
requestCameraPermission: function() {
const that = this;
wx.authorize({
scope: 'scope.camera',
success: () => {
// 用户同意授权
that.setData({
cameraPermissionGranted: true
});
},
fail: (err) => {
console.error('相机权限授权失败:', err);
that.showCameraWarning('请允许使用相机以体验AR功能');
}
});
},
// 显示相机警告
showCameraWarning: function(message) {
wx.showModal({
title: '相机提示',
content: message,
showCancel: false,
confirmText: '确定',
success: (res) => {
if (res.confirm) {
// 用户点击确定后的操作
console.log('用户确认相机提示');
}
}
});
},
handleReady: function ({detail}) {
// if (!this.data.cameraPermissionGranted || !this.data.hasFrontCamera) {
// this.showCameraWarning('无法启动前置相机,请检查设备是否支持和权限设置');
// return;
// }
console.log("user-xr handleReady");
console.log(detail.value);
console.log("scene 对象方法:", Object.getOwnPropertyNames(detail.value));
console.log("scene 对象原型方法:", Object.getOwnPropertyNames(Object.getPrototypeOf(detail.value)));
if(this.data.ObjectPath === ""){
this.setData({
ObjectPath:MarkerPath.object,
});
}
this.scene = detail.value;
const xrFrameSystem = wx.getXrFrameSystem();
this.camera = this.scene.getElementById('camera').getComponent(xrFrameSystem.Camera);
// this.mask = {el: this.scene.getElementById('mask'), color: 'rgba(44, 44, 44, 0.5)'};
const maskElement = this.scene.getElementById('mask');
if (maskElement) {
this.mask = {el: maskElement, color: 'rgba(44, 44, 44, 0.5)'};
console.log('Mask element found:', maskElement);
} else {
this.mask = null;
console.warn('Mask element not found in scene');
}
this.tmpV3 = new (xrFrameSystem.Vector3)();
console.log("user-xr handleReady:", {
scene: this.scene,
maskElementExists: !!maskElement,
sceneHasExportImage: typeof this.scene.exportImage === 'function',
sceneHasGetContainer: typeof this.scene.getContainer === 'function'
});
this.triggerEvent('ready', detail.value);
},
handleAssetsLoaded: function (detail) {
console.log('资源加载完成', detail.value);
if(!this.scene){
this.scene = detail.value;
const xrFrameSystem = wx.getXrFrameSystem();
this.camera = this.scene.getElementById('camera').getComponent(xrFrameSystem.Camera);
const maskElement = this.scene.getElementById('mask');
if (maskElement) {
this.mask = {el: maskElement, color: 'rgba(44, 44, 44, 0.5)'};
console.log('Mask element found after asset load:', maskElement);
}
}
console.log("user: "+ this.data.ObjectPath);
this.triggerEvent('assetsLoaded', detail.value);
},
handleTick: function({detail}) {
// this.mask && this.triggerEvent('syncPositions', [
// this.getScreenPosition(this.mask)
// ]);
if (this.mask && this.mask.el) {
this.triggerEvent('syncPositions', [
this.getScreenPosition(this.mask)
]);
} else {
// 可选:触发一个空的位置事件或者不触发
console.log('Mask element not ready yet');
}
},
handleTouchModel: function ({detail}) {
const {target} = detail.value;
this[target.id].color = `rgba(${Math.random()*255}, ${Math.random()*255}, ${Math.random()*255}, 0.5)`;
},
getScreenPosition: function(value) {
const {el, color} = value;
const xrFrameSystem = wx.getXrFrameSystem();
// console.log("getScreenPosition" + xrFrameSystem);
if (!el || typeof el.getComponent !== 'function') {
console.error('Invalid element:', el);
return [0, 0, color, ''];
}
const transformComponent = el.getComponent(xrFrameSystem.Transform);
if (!transformComponent) {
console.error('Transform component not found on element:', el);
return [0, 0, color, ''];
}
this.tmpV3.set(transformComponent.worldPosition);
// this.tmpV3.set(el.getComponent(xrFrameSystem.Transform).worldPosition);
const clipPos = this.camera.convertWorldPositionToClip(this.tmpV3);
const {frameWidth, frameHeight} = this.scene;
return [((clipPos.x + 1) / 2) * frameWidth, (1 - (clipPos.y + 1) / 2) * frameHeight, color, el.id];
},
takephoto:function(){
console.log("user-xr takephoto ");
console.log("调试信息:", {
scene: this.scene,
sceneType: typeof this.scene,
sceneIsNull: this.scene === null,
sceneIsUndefined: this.scene === undefined,
});
if (!this.scene) {
console.error('场景未初始化,请确保 handleReady 已被调用');
wx.showToast({
title: '场景未准备好',
icon: 'none'
});
return;
}
// 确保场景已经准备就绪
if (!this.camera) {
wx.showToast({
title: '相机未准备就绪',
icon: 'none'
});
return;
}
console.log("user-xr takephoto 2");
const that = this;
try {
// 方法1: 通过相机组件截图
if (this.camera && this.camera.el) {
console.log("使用相机组件截图");
// 获取相机元素的渲染目标
const cameraEl = this.camera.el;
if (cameraEl && typeof cameraEl.exportImage === 'function') {
cameraEl.exportImage({
success: function(res) {
console.log("相机截图成功:", res);
that.handlePhotoSuccess(res.tempFilePath);
},
fail: function(err) {
console.error('相机截图失败:', err);
that.tryAlternativeCapture();
}
});
} else {
// 方法2: 尝试通过场景的 canvas 截图
console.log("尝试通过场景 canvas 截图");
this.trySceneCanvasCapture();
}
} else {
console.log("尝试通过场景 canvas 截图");
this.trySceneCanvasCapture();
}
} catch (error) {
console.error('截图过程出错:', error);
this.tryAlternativeCapture();
}
},
tryAlternativeCapture: function() {
const that = this;
// 尝试通过 xr-camera 组件截图
if (this.camera && this.camera.el) {
console.log("尝试通过 camera 元素截图");
// 这里可以添加通过相机元素截图的逻辑
wx.showToast({
title: '截图功能暂不可用',
icon: 'none'
});
// 触发失败事件
this.triggerEvent('phototaken', {
success: false,
error: '截图功能不可用'
});
}
},
handlePhotoSuccess: function(filePath) {
const that = this;
this.setData({
tempImagePath: filePath
});
console.log("user-xr takephoto success: " + filePath);
this.saveImage();
// 触发事件将结果返回给父组件
this.triggerEvent('phototaken', {
success: true,
filePath: filePath
});
},
takephotoFromPage: function() {
// 这个方法需要从页面调用,通过 selectComponent 获取 XR 组件
// 然后调用组件的截图方法
try {
// 如果您的页面中有对组件的引用
if (this.scene && typeof this.scene.exportImage === 'function') {
this.scene.exportImage({
success: (res) => {
this.handlePhotoSuccess(res.tempFilePath);
},
fail: (err) => {
console.error('截图失败:', err);
this.triggerEvent('phototaken', {
success: false,
error: err
});
}
});
}
} catch (e) {
console.error('截图异常:', e);
}
},
saveImage:function(){
const that = this;
wx.getSetting({
success: (res) => {
if (!res.authSetting['scope.writePhotosAlbum']) {
// 没有权限,请求权限
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: () => {
// 授权成功,保存图片
that.saveImageToPhotosAlbum();
},
fail: (err) => {
console.error('相册授权失败', err);
// 引导用户打开授权设置
that.showAuthGuide();
}
});
} else {
// 已有权限,直接保存
that.saveImageToPhotosAlbum();
}
}
});
},
saveImageToPhotosAlbum:function(){
wx.saveImageToPhotosAlbum({
filePath: this.data.tempImagePath,
success: () => {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
},
fail: (err) => {
console.error('保存失败', err);
// 处理保存失败的特定情况
if (err.errMsg.includes('auth deny')) {
this.showAuthGuide();
} else {
wx.showToast({
title: '保存失败',
icon: 'none',
duration: 2000
});
}
}
});
},
showAuthGuide:function(){
wx.showModal({
title:'提示',
content:'需要您授权保存到相册权限',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
// 打开设置页面
wx.openSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.writePhotosAlbum']) {
wx.showToast({
title: '授权成功',
icon: 'success'
});
}
}
});
}
}
})
},
},
observers:{
'cameraPosition': function(eve) {
console.log('用户:', eve);
},
"currentState":function(state){
console.log('当前状态:', state);
if(this.data.currentState !== state){
this.setData({
currentState:state,
},()=>{
console.log("user-xr currentState:" + this.data.currentState);
if(state === SceneState.TakePhoto){
this.takephoto();
}
});
};
}
}
})请问哪里不对?
该功能暂不维护,如有 AR 需求,可使用 VisionKit 视觉能力