- 微信xr相机如何保存照片?
本人这边项目里有个需求,就是人脸检测后,将指定的模型附加到使用者的脸上,随后有一个拍摄照片功能,将附加模型后的用户的画面截取出来,并存储到手机本地中,现在人脸识别,以及相关模型也附加到人脸上,但是在拍摄上出现问题,总是保存失败,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(); } }); }; } } })请问哪里不对?
09-17 - 你们的demo在哪里?
https://developers.weixin.qq.com/miniprogram/dev/framework/xr-frame/ 你们的案例demo在什么位置,你们的开发文档之说查看demo,但是文档里根本没有 demo的地址,
09-08