Viro React图像跟踪异常:目标移出相机后AR图像未消失
React Native AR图像跟踪问题:目标移出视野后叠加图像未及时消失
我正在使用Viro React开发一款React Native AR应用,需求是实现图像跟踪功能,在识别到的目标图像上方显示另一张图像。目前遇到的问题是:有时当目标图像移出相机视野后,叠加的AR图像仍会停留在屏幕上,只有移动相机位置后,该AR图像才会消失。
package.json配置
{ "name": "ToothARcademy", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", "test": "jest" }, "dependencies": { "@expo/vector-icons": "^14.0.2", "@react-native-vector-icons/fontawesome": "^4.7.0-alpha.24", "@react-native-vector-icons/fontawesome6": "^6.6.0-alpha.27", "@react-navigation/bottom-tabs": "^6.6.1", "@react-navigation/native": "^6.1.18", "@react-navigation/native-stack": "^6.11.0", "@react-navigation/stack": "^6.4.1", "@reactvision/react-viro": "^2.41.4", "expo-font": "^12.0.9", "react": "18.2.0", "react-native": "0.73.3", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.18.1", "react-native-progress": "^5.0.1", "react-native-reanimated": "^3.15.0", "react-native-safe-area-context": "^4.10.8", "react-native-screens": "^3.34.0", "react-native-vector-icons": "^10.1.0", "react-native-zip-archive": "^7.0.1", "rn-fetch-blob": "^0.12.0" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@react-native/babel-preset": "0.73.20", "@react-native/eslint-config": "0.73.2", "@react-native/metro-config": "0.73.4", "@react-native/typescript-config": "0.73.1", "@types/react": "^18.3.3", "@types/react-native": "^0.72.8", "@types/react-native-vector-icons": "^6.4.18", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.6.3", "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", "react-test-renderer": "18.2.0", "typescript": "^5.5.4" }, "engines": { "node": ">=18" } }
ARExplorer.tsx代码
import React, { useEffect, useState } from "react"; import { ViroARScene, ViroARTrackingTargets, ViroARImageMarker, ViroImage, ViroAnimations, ViroTrackingStateConstants } from "@reactvision/react-viro"; import RNFS from 'react-native-fs'; import {DETAIL_PATHS,DIRECTORY_PATHS } from '../Statics/static-data' // Dynamically initialize AR assets const initARAssets = async () => { try { console.log("Creating AR tracking targets dynamically"); const documentDir = RNFS.DocumentDirectoryPath; const learnDir = DETAIL_PATHS; const files = await RNFS.readDir(DIRECTORY_PATHS.LEARN_IMAGES); const targets: { [key: string]: any } = {}; files.forEach(file => { console.log(file.path); // Identify target images by checking if the name ends with any image extension const targetMatch = file.name.match(/^Learn_Image(\d+)(\.[a-zA-Z]+)$/); if (targetMatch) { const key = `Learn_Image${targetMatch[1]}`; targets[key] = { source: { uri:'file://'+ file.path }, orientation: "Up", physicalWidth: 0.165, }; } }); ViroARTrackingTargets.createTargets(targets); console.log("AR tracking targets created:", targets); // ViroAnimations.registerAnimations({ // scaleUp: { // properties: { scaleX: 1, scaleY: 1, scaleZ: 1 }, // duration: 500, // easing: "bounce", // }, // scaleDown: { // properties: { scaleX: 0, scaleY: 0, scaleZ: 0 }, // duration: 200, // }, // scaleButton: { // properties: { scaleX: 0.09, scaleY: 0.09, scaleZ: 0.09 }, // duration: 500, // easing: "bounce", // }, // scaleSphereUp: { // properties: { scaleX: 0.8, scaleY: 0.8, scaleZ: 0.8 }, // duration: 50, // easing: "easeineaseout", // }, // scaleSphereDown: { // properties: { scaleX: 1, scaleY: 1, scaleZ: 1 }, // duration: 50, // easing: "easeineaseout", // }, // }); } catch (error) { console.error('Error initializing AR assets:', error); } }; const ARExplorer = () => { const [activeMarker, setActiveMarker] = useState<string | null>(null); // Store the active marker's key const [imageSources, setImageSources] = useState<{ [key: string]: string }>({}); useEffect(() => { initARAssets(); loadImagesFromDocumentDir(); }, []); const loadImagesFromDocumentDir = async () => { try { const files = await RNFS.readDir(DIRECTORY_PATHS.LEARN_IMAGES); const sources: { [key: string]: string } = {}; files.forEach(file => { const annotedMatch = file.name.match(/^Learn_Image(\d+)_Annotated(\.[a-zA-Z]+)$/); if (annotedMatch) { const key = `Learn_Image${annotedMatch[1]}`; sources[key] = file.path; } }); setImageSources(sources); } catch (error) { console.error('Error loading images from document directory:', error); } }; const onTrackingUpdated = (state: any) => { if (state === ViroTrackingStateConstants.TRACKING_UNAVAILABLE) { setActiveMarker(null); // Reset the active marker if tracking is unavailable } }; const handleAnchorFound = (markerId: string) => { if (activeMarker !== markerId) { console.log("Anchor found for:", markerId); // Clear the previous marker setActiveMarker(null); // Allow time for the state to clear setTimeout(() => { setActiveMarker(markerId); // Set the new marker as active }, 100); } else{ setActiveMarker(markerId); } }; const handleAnchorRemoved = (markerId: string) => { if (activeMarker === markerId) { console.log("Anchor removed for:", markerId); setActiveMarker(null); // Reset the active marker when it is removed } }; const handleAnchorNotViewed = (markerId: string) => { if (activeMarker === markerId) { console.log("Anchor no longer viewed:", markerId); setActiveMarker(null); // Reset the active marker when it is no longer viewed } }; return ( <ViroARScene onTrackingUpdated={onTrackingUpdated}> {Object.keys(imageSources).map(key => ( <ViroARImageMarker key={key} target={key} onAnchorFound={() => handleAnchorFound(key)} onAnchorRemoved={() => handleAnchorRemoved(key)} onAnchorUpdated={(anchor) => { if (anchor.trackingMethod === "tracking") { handleAnchorFound(key); } else if (anchor.trackingMethod === "lastKnownPose") { handleAnchorNotViewed(key); } }} > {activeMarker === key && imageSources[key] && ( <ViroImage source={{ uri: 'file://' + imageSources[key] }} scale={[0.18, 0.18, -0.7]} rotation={[-90, 0, 0]} /> )} </ViroARImageMarker> ))} </ViroARScene> ); }; export default ARExplorer;
问题分析与解决方案
问题根源
- Viro的
onAnchorRemoved和lastKnownPose回调触发存在延迟,当目标快速移出视野时,无法及时通知状态变更。 - 切换标记时的
setTimeout延迟会加剧状态更新滞后,导致图像残留。 - 全局
onTrackingUpdated仅处理整体跟踪失效,无法覆盖单个标记的跟踪丢失场景。
修复方案
1. 移除状态更新延迟
删除切换标记时的setTimeout,直接更新activeMarker,避免状态滞后:
const handleAnchorFound = (markerId: string) => { if (activeMarker !== markerId) { console.log("Anchor found for:", markerId); setActiveMarker(markerId); } };
2. 优化跟踪状态判断
在onAnchorUpdated中直接通过anchor.isTracking判断跟踪状态,及时隐藏图像:
const handleAnchorLost = (markerId: string) => { if (activeMarker === markerId) { console.log("Anchor lost for:", markerId); setActiveMarker(null); } }; // 组件内的ViroARImageMarker部分 <ViroARImageMarker key={key} target={key} onAnchorFound={() => handleAnchorFound(key)} onAnchorRemoved={() => handleAnchorLost(key)} onAnchorUpdated={(anchor) => { if (anchor.isTracking) { handleAnchorFound(key); } else { handleAnchorLost(key); } }} > {/* 子组件内容 */} </ViroARImageMarker>
3. 额外优化建议
- 升级
@reactvision/react-viro到最新稳定版,修复已知的跟踪回调延迟问题。 - 确保目标图像的
physicalWidth设置准确,不准确的物理尺寸会导致跟踪不稳定。 - 可添加短时间防抖处理(如100ms),避免跟踪状态频繁切换导致图像闪烁,但不要过度延迟状态更新。
内容的提问来源于stack exchange,提问作者safiqul islam
相关产品推荐
相关产品推荐

