Skip to main content

r3f-animation

React Three Fiber animation - useFrame, useAnimations, spring physics, keyframes. Use when animating objects, playing GLTF animations, creating procedural motion, or implementing physics-based movement.

소스 정보

저장소
zebbern/claude-code-guide
최근 소스 활동
2026년 5월 31일 15:44
감지된 SKILL.md 언어
영어
스타
4,677
포크
472

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
r3f-animation
description
React Three Fiber animation - useFrame, useAnimations, spring physics, keyframes. Use when animating objects, playing GLTF animations, creating procedural motion, or implementing physics-based movement.
# React Three Fiber Animation ## Quick Start ```tsx import { Canvas, useFrame } from '@react-three/fiber' import { useRef } from 'react' function RotatingBox() { const meshRef = useRef() useFrame((state, delta) => { meshRef.current.rotation.x += delta meshRef.current.rotation.y += delta * 0.5 }) return ( <mesh ref={meshRef}> <boxGeometry /> <meshStandardMaterial color="hotpink" /> </mesh> ) } export default function App() { return ( <Canvas> <ambientLight /> <RotatingBox /> </Canvas> ) } ``` ## useFrame Hook The core animation hook in R3F. Runs every frame. ### Basic Usage ```tsx import { useFrame } from '@react-three/fiber' import { useRef } from 'react' function AnimatedMesh() { const meshRef = useRef() useFrame((state, delta) => { // state contains: clock, camera, scene, gl, mouse, etc. // delta is time since last frame in seconds meshRef.current.rotation.y += delta }) return ( <mesh ref={meshRef}> <boxGeometry /> <meshStandardMaterial color="orange" /> </mesh> ) } ``` ### State Object ```tsx useFrame((state, delta, xrFrame) => { const { clock, // THREE.Clock camera, // Current camera scene, // Scene gl, // WebGLRenderer mouse, // Normalized mouse position (-1 to 1) pointer, // Same as mouse viewport, // Viewport dimensions size, // Canvas size raycaster, // Raycaster get, // Get current state set, // Set state invalidate, // Request re-render (when frameloop="demand") } = state // Time-based animation const t = clock.getElapsedTime() meshRef.current.position.y = Math.sin(t) * 2 }) ``` ### Render Priority ```tsx // Lower numbers run first. Default is 0. // Use negative for pre-render, positive for post-render function PreRender() { useFrame(() => { // Runs before main render }, -1) } function PostRender() { useFrame(() => { // Runs after main render }, 1) } function DefaultRender() { useFrame(() => { // Runs at default priority (0) }) } ``` ### Conditional Animation ```tsx function ConditionalAnimation({ isAnimating }) { const meshRef = useRef() useFrame((state, delta) => { if (!isAnimating) return meshRef.current.rotation.y += delta }) return <mesh ref={meshRef}>...</mesh> } ``` ## GLTF Animations with useAnimations The recommended way to play animations from GLTF/GLB files. ### Basic Usage ```tsx import { useGLTF, useAnimations } from '@react-three/drei' import { useEffect, useRef } from 'react' function AnimatedModel() { const group = useRef() const { scene, animations } = useGLTF('/models/character.glb') const { actions, names } = useAnimations(animations, group) useEffect(() => { // Play first animation actions[names[0]]?.play() }, [actions, names]) return <primitive ref={group} object={scene} /> } ``` ### Animation Control ```tsx function Character() { const group = useRef() const { scene, animations } = useGLTF('/models/character.glb') const { actions, mixer } = useAnimations(animations, group) useEffect(() => { const action = actions['Walk'] if (action) { // Playback control action.play() action.stop() action.reset() action.paused = true // Speed action.timeScale = 1.5 // 1.5x speed action.timeScale = -1 // Reverse // Loop modes action.loop = THREE.LoopOnce action.loop = THREE.LoopRepeat action.loop = THREE.LoopPingPong action.repetitions = 3 action.clampWhenFinished = true // Weight (for blending) action.weight = 1 } }, [actions]) return <primitive ref={group} object={scene} /> } ``` ### Crossfade Between Animations ```tsx import { useGLTF, useAnimations } from '@react-three/drei' import { useState, useEffect, useRef } from 'react' function Character() { const group = useRef() const { scene, animations } = useGLTF('/models/character.glb') const { actions } = useAnimations(animations, group) const [currentAnim, setCurrentAnim] = useState('Idle') useEffect(() => { // Fade out all animations Object.values(actions).forEach(action => { action?.fadeOut(0.5) }) // Fade in current animation actions[currentAnim]?.reset().fadeIn(0.5).play() }, [currentAnim, actions]) return ( <group ref={group}> <primitive object={scene} /> </group> ) } ``` ### Animation Events ```tsx function AnimatedModel() { const group = useRef() const { scene, animations } = useGLTF('/models/character.glb') const { actions, mixer } = useAnimations(animations, group) useEffect(() => { // Listen for animation events const onFinished = (e) => { console.log('Animation finished:', e.action.getClip().name) } const onLoop = (e) => { console.log('Animation looped:', e.action.getClip().name) } mixer.addEventListener('finished', onFinished) mixer.addEventListener('loop', onLoop) return () => { mixer.removeEventListener('finished', onFinished) mixer.removeEventListener('loop', onLoop) } }, [mixer]) return <primitive ref={group} object={scene} /> } ``` ### Animation Blending ```tsx function CharacterController({ speed = 0 }) { const group = useRef() const { scene, animations } = useGLTF('/models/character.glb') const { actions } = useAnimations(animations, group) useEffect(() => { // Start all animations actions['Idle']?.play() actions['Walk']?.play() actions['Run']?.play() }, [actions]) // Blend based on speed useFrame(() => { if (speed < 0.1) { actions['Idle']?.setEffectiveWeight(1) actions['Walk']?.setEffectiveWeight(0) actions['Run']?.setEffectiveWeight(0) } else if (speed < 5) { const t = speed / 5 actions['Idle']?.setEffectiveWeight(1 - t) actions['Walk']?.setEffectiveWeight(t) actions['Run']?.setEffectiveWeight(0) } else { const t = Math.min((speed - 5) / 5, 1) actions['Idle']?.setEffectiveWeight(0) actions['Walk']?.setEffectiveWeight(1 - t) actions['Run']?.setEffectiveWeight(t) } }) return <primitive ref={group} object={scene} /> } ``` ## Spring Animation (@react-spring/three) Physics-based spring animations that integrate with R3F. ### Installation ```bash npm install @react-spring/three ``` ### Basic Spring ```tsx import { useSpring, animated } from '@react-spring/three' function AnimatedBox() { const [active, setActive] = useState(false) const { scale, color } = useSpring({ scale: active ? 1.5 : 1, color: active ? '#ff6b6b' : '#4ecdc4', config: { mass: 1, tension: 280, friction: 60 } }) return ( <animated.mesh scale={scale} onClick={() => setActive(!active)} > <boxGeometry /> <animated.meshStandardMaterial color={color} /> </animated.mesh> ) } ``` ### Spring Config Presets ```tsx import { useSpring, animated, config } from '@react-spring/three' function SpringPresets() { const { position } = useSpring({ position: [0, 2, 0], config: config.wobbly // Presets: default, gentle, wobbly, stiff, slow, molasses }) // Or custom config const { rotation } = useSpring({ rotation: [0, Math.PI, 0], config: { mass: 1, tension: 170, friction: 26, clamp: false, precision: 0.01, velocity: 0, } }) return ( <animated.mesh position={position} rotation={rotation}> <boxGeometry /> <meshStandardMaterial /> </animated.mesh> ) } ``` ### Multiple Springs ```tsx import { useSprings, animated } from '@react-spring/three' function AnimatedBoxes({ count = 5 }) { const [springs, api] = useSprings(count, (i) => ({ position: [i * 2 - count, 0, 0], scale: 1, config: { mass: 1, tension: 280, friction: 60 } })) const handleClick = (index) => { api.start((i) => { if (i === index) return { scale: 1.5 } return { scale: 1 } }) } return springs.map((spring, i) => ( <animated.mesh key={i} position={spring.position} scale={spring.scale} onClick={() => handleClick(i)} > <boxGeometry /> <meshStandardMaterial color="orange" /> </animated.mesh> )) } ``` ### Gesture Integration ```tsx import { useSpring, animated } from '@react-spring/three' import { useDrag } from '@use-gesture/react' function DraggableBox() { const [spring, api] = useSpring(() => ({ position: [0, 0, 0], config: { mass: 1, tension: 280, friction: 60 } })) const bind = useDrag(({ movement: [mx, my], down }) => { api.start({ position: down ? [mx / 100, -my / 100, 0] : [0, 0, 0] }) }) return ( <animated.mesh {...bind()} position={spring.position}> <boxGeometry /> <meshStandardMaterial color="hotpink" /> </animated.mesh> ) } ``` ### Chain Animations ```tsx import { useSpring, animated, useChain, useSpringRef } from '@react-spring/three' function ChainedAnimation() { const scaleRef = useSpringRef() const rotationRef = useSpringRef() const { scale } = useSpring({ ref: scaleRef, from: { scale: 0 }, to: { scale: 1 }, config: { tension: 200, friction: 20 } }) const { rotation } = useSpring({ ref: rotationRef, from: { rotation: [0, 0, 0] }, to: { rotation: [0, Math.PI * 2, 0] }, config: { tension: 100, friction: 30 } }) // Scale first (0-0.5), then rotation (0.5-1) useChain([scaleRef, rotationRef], [0, 0.5]) return ( <animated.mesh scale={scale} rotation={rotation}> <boxGeometry />
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기