Three.js中通过按钮实现3D房间对象位置轮换的技术求助
实现方案
核心思路
- 先保存所有房间的初始位置快照,避免后续位置修改影响原始坐标的获取
- 针对两个按钮的偏移需求,通过索引取模运算实现循环位置映射:
- +1按钮:每个房间移动到下一个房间的初始位置,最后一个房间回到第一个的初始位置
- +2按钮:每个房间移动到隔一个房间的初始位置,最后两个房间依次填补前两个的位置
具体代码实现(基于你的PartOne组件修改)
import React, { useRef, useEffect } from "react"; import * as THREE from "three"; import { useThree } from "@react-three/fiber"; export default function PartOne() { const { scene } = useThree(); const roomsRef = useRef([]); // 存储所有房间的mesh实例引用 const roomInitialPositions = useRef([]); // 存储所有房间的初始位置快照 // 创建单个房间的工具函数 const createRoom = (posX, posZ, index) => { const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); const roomMesh = new THREE.Mesh(geometry, material); roomMesh.position.set(posX, 0, posZ); roomsRef.current[index] = roomMesh; scene.add(roomMesh); }; // 初始化房间与位置快照 useEffect(() => { // 按顺序创建ROOM1到ROOM8,对应索引0-7 createRoom(-10, 10, 0); createRoom(0, 10, 1); createRoom(10, 10, 2); createRoom(10, 0, 3); createRoom(10, -10, 4); createRoom(0, -10, 5); createRoom(-10, -10, 6); createRoom(-10, 0, 7); // 保存初始位置(必须克隆,避免引用同步修改) roomInitialPositions.current = roomsRef.current.map(room => room.position.clone()); // 组件卸载时清理场景 return () => { roomsRef.current.forEach(room => scene.remove(room)); }; }, [scene]); // +1按钮点击逻辑 const handleShiftPlus1 = () => { const offset = 1; roomsRef.current.forEach((room, idx) => { // 计算目标位置索引:(当前索引+偏移量) 对房间总数取模,实现循环 const targetPosIndex = (idx + offset) % 8; const targetPosition = roomInitialPositions.current[targetPosIndex]; // 直接赋值位置(如需平滑动画,可替换为GSAP/Tween.js) room.position.copy(targetPosition); }); }; // +2按钮点击逻辑 const handleShiftPlus2 = () => { const offset = 2; roomsRef.current.forEach((room, idx) => { const targetPosIndex = (idx + offset) % 8; const targetPosition = roomInitialPositions.current[targetPosIndex]; room.position.copy(targetPosition); }); }; return ( <div style={{ position: "absolute", top: 20, left: 20, zIndex: 100 }}> <button onClick={handleShiftPlus1} style={{ marginRight: 12, padding: "8px 16px", fontSize: 16 }} > increase + 1 </button> <button onClick={handleShiftPlus2} style={{ padding: "8px 16px", fontSize: 16 }} > increase + 2 </button> </div> ); }
关键细节说明
- 位置快照的必要性:用
room.position.clone()而不是直接存room.position,因为Three.js的Vector3是引用类型,直接存储会导致后续修改房间位置时,快照数组里的坐标也跟着变化,无法获取原始位置。 - 循环映射的实现:
(idx + offset) % 8是核心逻辑,通过取模运算让索引超出范围后自动回到开头,实现8个房间的循环位置替换。 - 平滑动画扩展:如果需要更流畅的移动效果,可以引入GSAP库,将
room.position.copy(targetPosition)替换为:import gsap from "gsap"; // ... gsap.to(room.position, { x: targetPosition.x, y: targetPosition.y, z: targetPosition.z, duration: 0.6, ease: "power2.inOut" });
内容的提问来源于stack exchange,提问作者Sayedul Karim
相关产品推荐
相关产品推荐

