Flutter中如何通过点击3D模型获取坐标(基于model_viewer_plus)
解决model_viewer_plus点击3D模型获取坐标的问题
问题分析
你当前的代码存在几个导致功能失效的核心问题:
- 重复调用
runJavaScript添加点击事件监听,造成逻辑冲突 - 未等待3D模型加载完成就绑定事件,此时模型元素可能还未就绪
- 使用
event.clientX/clientY获取的是浏览器窗口级坐标,而非模型容器内的相对坐标,导致点位计算偏差
修正后的代码方案
替换你现有onWebViewCreated部分的代码,核心优化了事件绑定时机、坐标取值逻辑,同时简化了冗余代码:
ModelViewer( src: 'assets/models/feet.glb', alt: '3D足部模型', id: "feet", ar: true, disableZoom: true, cameraControls: true, onWebViewCreated: (controller) { // 仅保留一个JavaScript通道用于接收点击数据 controller.addJavaScriptChannel( "ModelClickChannel", onMessageReceived: (message) { log("点击坐标数据: ${message.message}"); setState(() { tappedPosition = message.message; // 可选:解析JSON为具体坐标对象 // final coords = jsonDecode(message.message); // final x = coords['position']['x']; // final y = coords['position']['y']; // final z = coords['position']['z']; }); }, ); // 等待模型加载完成后再绑定点击事件 controller.runJavaScript(''' const modelViewer = document.getElementById('feet'); modelViewer.addEventListener('load', () => { modelViewer.addEventListener('click', (event) => { // 使用offsetX/offsetY获取模型容器内的相对坐标 const intersection = modelViewer.positionAndNormalFromPoint(event.offsetX, event.offsetY); if (intersection) { // 发送包含位置和法向量的完整数据 ModelClickChannel.postMessage(JSON.stringify({ position: intersection.position, normal: intersection.normal })); } }); }); '''); }, )
关键优化点
- 等待模型加载就绪:通过
load事件确保模型完全加载后再绑定点击逻辑,避免元素未就绪导致的监听失效 - 使用相对坐标:用
event.offsetX/offsetY替代窗口级坐标,保证点位计算的准确性 - 简化逻辑:移除重复的事件监听代码,避免逻辑冲突
- 结构化数据:发送包含位置和法向量的JSON数据,方便后续解析存储
额外注意事项
- 确认你的
feet.glb模型材质未设置不可交互属性,否则无法触发点击事件 - 测试时可先打印
intersection的完整内容,确认是否能正确获取到三维坐标 - 若需将坐标发送给医生,建议解析后将
x/y/z三维值存入本地存储或上传至后端服务
内容的提问来源于stack exchange,提问作者Munsif Ali
相关产品推荐
相关产品推荐

