import { useEffect, useMemo, useRef, useState } from 'react' import { Canvas, useFrame, useThree } from '@react-three/fiber' import * as THREE from 'three' const STEPS = [ { label: 'Step 1: Group at rest', detail: 'Parent group is centered and children keep their offsets.', cameraPosition: [0, 1.7, 6], lookAt: [0, 0, 0], groupPosition: [0, 0, 0], groupRotation: [0, 0, 0], spinSpeed: 0, }, { label: 'Step 2: Parent rotation', detail: 'Only parent rotates; children move together without changing local layout.', cameraPosition: [1.7, 1.8, 5.6], lookAt: [0, 0, 0], groupPosition: [0, 0, 0], groupRotation: [0.15, 0.55, 0], spinSpeed: 0.015, }, { label: 'Step 3: Parent move + rotate', detail: 'Parent shifts and rotates as one unit while child spacing stays intact.', cameraPosition: [0.6, 2.4, 7], lookAt: [0.4, 0.35, 0], groupPosition: [0.7, 0.4, -0.4], groupRotation: [0.35, 1.2, 0.2], spinSpeed: 0.03, }, ] function GuidedGroupScene({ stepIndex }) { const { camera } = useThree() const groupRef = useRef() const targets = useMemo( () => ({ cameraPosition: new THREE.Vector3(), cameraLookAt: new THREE.Vector3(), lookCurrent: new THREE.Vector3(), groupPosition: new THREE.Vector3(), groupRotation: new THREE.Euler(), spinSpeed: 0, initialized: false, }), [] ) useEffect(() => { const step = STEPS[stepIndex] targets.cameraPosition.set(...step.cameraPosition) targets.cameraLookAt.set(...step.lookAt) targets.groupPosition.set(...step.groupPosition) targets.groupRotation.set(...step.groupRotation) targets.spinSpeed = step.spinSpeed if (groupRef.current && !targets.initialized) { camera.position.copy(targets.cameraPosition) targets.lookCurrent.copy(targets.cameraLookAt) groupRef.current.position.copy(targets.groupPosition) groupRef.current.rotation.copy(targets.groupRotation) targets.initialized = true } }, [camera, stepIndex, targets]) useFrame(() => { if (!groupRef.current) return groupRef.current.position.lerp(targets.groupPosition, 0.1) groupRef.current.rotation.x += (targets.groupRotation.x - groupRef.current.rotation.x) * 0.1 groupRef.current.rotation.y += (targets.groupRotation.y - groupRef.current.rotation.y) * 0.1 groupRef.current.rotation.z += (targets.groupRotation.z - groupRef.current.rotation.z) * 0.1 groupRef.current.rotation.y += targets.spinSpeed * 0.2 camera.position.lerp(targets.cameraPosition, 0.08) targets.lookCurrent.lerp(targets.cameraLookAt, 0.08) camera.lookAt(targets.lookCurrent) }) return ( <> ) } export default function App() { const [stepIndex, setStepIndex] = useState(0) const step = STEPS[stepIndex] useEffect(() => { function onKeyDown(event) { if (event.key === '1') setStepIndex(0) if (event.key === '2') setStepIndex(1) if (event.key === '3') setStepIndex(2) } window.addEventListener('keydown', onKeyDown) return () => window.removeEventListener('keydown', onKeyDown) }, []) return (

Task 4: Group Motion

{step.label}

{step.detail}

{STEPS.map((_, index) => ( ))}

Simple parent group animation. Keys: 1, 2, 3.

) }