Skip to main content 首页 创作者 beko2210 firstbrain threejs-animation
threejs-animation Three.js animation - keyframe animation, skeletal animation, morph targets, animation mixing. Use when animating objects, playing GLTF animations, creating procedural motion, or blending animations.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/BEKO2210/Firstbrain --skill threejs-animation命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name threejs-animation description Three.js animation - keyframe animation, skeletal animation, morph targets, animation mixing. Use when animating objects, playing GLTF animations, creating procedural motion, or blending animations. type skill created 2026-02-27T00:00:00.000Z domain design-creative category 3d-animation risk unknown source community tags ["skill","design-creative","3d-animation","threejs","animation"]
Three.js Animation
When to Use
You need to animate objects, rigs, morph targets, or imported GLTF animations in Three.js.
The task involves mixers, clips, keyframes, procedural motion, or animation blending.
You are building motion behavior in a Three.js scene rather than just static rendering.
Quick Start
import * as THREE from "three" ;
const timer = new THREE .Timer ();
renderer.setAnimationLoop (() => {
timer.update ();
const delta = timer.getDelta ();
const elapsed = timer.getElapsed ();
mesh.rotation .y += delta;
mesh.position .y = Math .sin (elapsed) * 0.5 ;
renderer.render (scene, camera);
});
Note: THREE.Timer is recommended over THREE.Clock as of r183. Timer pauses when the page is hidden and has a cleaner API. THREE.Clock still works but is considered legacy.
Animation System Overview
Three.js animation system has three main components:
AnimationClip - Container for keyframe data
AnimationMixer - Plays animations on a root object
AnimationAction - Controls playback of a clip
AnimationClip
Stores keyframe animation data.
const times = [ , , ];
values = [ , , ];
track = . (
,
times,
values,
);
clip = . ( , , [track]);
0
1
2
const
0
1
0
const
new
THREE
NumberKeyframeTrack
".position[y]"
const
new
THREE
AnimationClip
"bounce"
2
KeyframeTrack Types
new THREE .NumberKeyframeTrack (".opacity" , times, [1 , 0 ]);
new THREE .NumberKeyframeTrack (".material.opacity" , times, [1 , 0 ]);
new THREE .VectorKeyframeTrack (".position" , times, [
0 ,
0 ,
0 ,
1 ,
2 ,
0 ,
0 ,
0 ,
0 ,
]);
const q1 = new THREE .Quaternion ().setFromEuler (new THREE .Euler (0 , 0 , 0 ));
const q2 = new THREE .Quaternion ().setFromEuler (new THREE .Euler (0 , Math .PI , 0 ));
new THREE .QuaternionKeyframeTrack (
".quaternion" ,
[0 , 1 ],
[q1.x , q1.y , q1.z , q1.w , q2.x , q2.y , q2.z , q2.w ],
);
new THREE .ColorKeyframeTrack (".material.color" , times, [
1 ,
0 ,
0 ,
0 ,
1 ,
0 ,
0 ,
0 ,
1 ,
]);
new THREE .BooleanKeyframeTrack (".visible" , [0 , 0.5 , 1 ], [true , false , true ]);
new THREE .StringKeyframeTrack (
".morphTargetInfluences[smile]" ,
[0 , 1 ],
["0" , "1" ],
);
Interpolation Modes const track = new THREE .VectorKeyframeTrack (".position" , times, values);
track.setInterpolation (THREE .InterpolateLinear );
track.setInterpolation (THREE .InterpolateSmooth );
track.setInterpolation (THREE .InterpolateDiscrete );
BezierInterpolant (r183) Three.js r183 adds THREE.BezierInterpolant for bezier curve interpolation in keyframe tracks, enabling smoother animation curves with tangent control.
AnimationMixer Plays animations on an object and its descendants.
const mixer = new THREE .AnimationMixer (model);
const action = mixer.clipAction (clip);
action.play ();
function animate ( ) {
const delta = clock.getDelta ();
mixer.update (delta);
requestAnimationFrame (animate);
renderer.render (scene, camera);
}
Mixer Events mixer.addEventListener ("finished" , (e ) => {
console .log ("Animation finished:" , e.action .getClip ().name );
});
mixer.addEventListener ("loop" , (e ) => {
console .log ("Animation looped:" , e.action .getClip ().name );
});
AnimationAction Controls playback of an animation clip.
const action = mixer.clipAction (clip);
action.play ();
action.stop ();
action.reset ();
action.halt (fadeOutDuration);
action.isRunning ();
action.isScheduled ();
action.time = 0.5 ;
action.timeScale = 1 ;
action.paused = false ;
action.weight = 1 ;
action.setEffectiveWeight (1 );
action.loop = THREE .LoopRepeat ;
action.loop = THREE .LoopOnce ;
action.loop = THREE .LoopPingPong ;
action.repetitions = 3 ;
action.clampWhenFinished = true ;
action.blendMode = THREE .NormalAnimationBlendMode ;
action.blendMode = THREE .AdditiveAnimationBlendMode ;
Fade In/Out
action.reset ().fadeIn (0.5 ).play ();
action.fadeOut (0.5 );
const action1 = mixer.clipAction (clip1);
const action2 = mixer.clipAction (clip2);
action1.play ();
action1.crossFadeTo (action2, 0.5 , true );
action2.play ();
Loading GLTF Animations Most common source of skeletal animations.
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js" ;
const loader = new GLTFLoader ();
loader.load ("model.glb" , (gltf ) => {
const model = gltf.scene ;
scene.add (model);
const mixer = new THREE .AnimationMixer (model);
const clips = gltf.animations ;
console .log (
"Available animations:" ,
clips.map ((c ) => c.name ),
);
if (clips.length > 0 ) {
const action = mixer.clipAction (clips[0 ]);
action.play ();
}
const walkClip = THREE .AnimationClip .findByName (clips, "Walk" );
if (walkClip) {
mixer.clipAction (walkClip).play ();
}
window .mixer = mixer;
});
function animate ( ) {
const delta = clock.getDelta ();
if (window .mixer ) window .mixer .update (delta);
requestAnimationFrame (animate);
renderer.render (scene, camera);
}
Skeletal Animation
Skeleton and Bones
const skinnedMesh = model.getObjectByProperty ("type" , "SkinnedMesh" );
const skeleton = skinnedMesh.skeleton ;
skeleton.bones .forEach ((bone ) => {
console .log (bone.name , bone.position , bone.rotation );
});
const headBone = skeleton.bones .find ((b ) => b.name === "Head" );
if (headBone) headBone.rotation .y = Math .PI / 4 ;
const helper = new THREE .SkeletonHelper (model);
scene.add (helper);
Programmatic Bone Animation function animate ( ) {
const time = clock.getElapsedTime ();
const headBone = skeleton.bones .find ((b ) => b.name === "Head" );
if (headBone) {
headBone.rotation .y = Math .sin (time) * 0.3 ;
}
mixer.update (clock.getDelta ());
}
Bone Attachments
const weapon = new THREE .Mesh (weaponGeometry, weaponMaterial);
const handBone = skeleton.bones .find ((b ) => b.name === "RightHand" );
if (handBone) handBone.add (weapon);
weapon.position .set (0 , 0 , 0.5 );
weapon.rotation .set (0 , Math .PI / 2 , 0 );
Morph Targets Blend between different mesh shapes.
const geometry = mesh.geometry ;
console .log ("Morph attributes:" , Object .keys (geometry.morphAttributes ));
mesh.morphTargetInfluences ;
mesh.morphTargetDictionary ;
mesh.morphTargetInfluences [0 ] = 0.5 ;
const smileIndex = mesh.morphTargetDictionary ["smile" ];
mesh.morphTargetInfluences [smileIndex] = 1 ;
Animating Morph Targets
function animate ( ) {
const t = clock.getElapsedTime ();
mesh.morphTargetInfluences [0 ] = (Math .sin (t) + 1 ) / 2 ;
}
const track = new THREE .NumberKeyframeTrack (
".morphTargetInfluences[smile]" ,
[0 , 0.5 , 1 ],
[0 , 1 , 0 ],
);
const clip = new THREE .AnimationClip ("smile" , 1 , [track]);
mixer.clipAction (clip).play ();
Animation Blending Mix multiple animations together.
const idleAction = mixer.clipAction (idleClip);
const walkAction = mixer.clipAction (walkClip);
const runAction = mixer.clipAction (runClip);
idleAction.play ();
walkAction.play ();
runAction.play ();
idleAction.setEffectiveWeight (1 );
walkAction.setEffectiveWeight (0 );
runAction.setEffectiveWeight (0 );
function updateAnimations (speed ) {
if (speed < 0.1 ) {
idleAction.setEffectiveWeight (1 );
walkAction.setEffectiveWeight (0 );
runAction.setEffectiveWeight (0 );
} else if (speed < 5 ) {
const t = speed / 5 ;
idleAction.setEffectiveWeight (1 - t);
walkAction.setEffectiveWeight (t);
runAction.setEffectiveWeight (0 );
} else {
const t = Math .min ((speed - 5 ) / 5 , 1 );
idleAction.setEffectiveWeight (0 );
walkAction.setEffectiveWeight (1 - t);
runAction.setEffectiveWeight (t);
}
}
Additive Blending
const baseAction = mixer.clipAction (baseClip);
baseAction.play ();
const additiveAction = mixer.clipAction (additiveClip);
additiveAction.blendMode = THREE .AdditiveAnimationBlendMode ;
additiveAction.play ();
THREE .AnimationUtils .makeClipAdditive (additiveClip);
Animation Utilities import * as THREE from "three" ;
const clip = THREE .AnimationClip .findByName (clips, "Walk" );
const subclip = THREE .AnimationUtils .subclip (clip, "subclip" , 0 , 30 , 30 );
THREE .AnimationUtils .makeClipAdditive (clip);
THREE .AnimationUtils .makeClipAdditive (clip, 0 , referenceClip);
const clone = clip.clone ();
clip.duration ;
clip.optimize ();
clip.resetDuration ();
Procedural Animation Patterns
Smooth Damping
const target = new THREE .Vector3 ();
const current = new THREE .Vector3 ();
const velocity = new THREE .Vector3 ();
function smoothDamp (current, target, velocity, smoothTime, deltaTime ) {
const omega = 2 / smoothTime;
const x = omega * deltaTime;
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
const change = current.clone ().sub (target);
const temp = velocity
.clone ()
.add (change.clone ().multiplyScalar (omega))
.multiplyScalar (deltaTime);
velocity.sub (temp.clone ().multiplyScalar (omega)).multiplyScalar (exp);
return target.clone ().add (change.add (temp).multiplyScalar (exp));
}
function animate ( ) {
current.copy (smoothDamp (current, target, velocity, 0.3 , delta));
mesh.position .copy (current);
}
Spring Physics class Spring {
constructor (stiffness = 100 , damping = 10 ) {
this .stiffness = stiffness;
this .damping = damping;
this .position = 0 ;
this .velocity = 0 ;
this .target = 0 ;
}
update (dt ) {
const force = -this .stiffness * (this .position - this .target );
const dampingForce = -this .damping * this .velocity ;
this .velocity += (force + dampingForce) * dt;
this .position += this .velocity * dt;
return this .position ;
}
}
const spring = new Spring (100 , 10 );
spring.target = 1 ;
function animate ( ) {
mesh.position .y = spring.update (delta);
}
Oscillation function animate ( ) {
const t = clock.getElapsedTime ();
mesh.position .y = Math .sin (t * 2 ) * 0.5 ;
mesh.position .y = Math .abs (Math .sin (t * 3 )) * 2 ;
mesh.position .x = Math .cos (t) * 2 ;
mesh.position .z = Math .sin (t) * 2 ;
mesh.position .x = Math .sin (t) * 2 ;
mesh.position .z = Math .sin (t * 2 ) * 1 ;
}
Performance Tips
Share clips : Same AnimationClip can be used on multiple mixers
Optimize clips : Call clip.optimize() to remove redundant keyframes
Disable when off-screen : Stop mixer updates for invisible objects
Use LOD for animations : Simpler rigs for distant characters
Limit active mixers : Each mixer.update() has a cost
mesh.onBeforeRender = () => {
action.paused = false ;
};
mesh.onAfterRender = () => {
if (!isInFrustum (mesh)) {
action.paused = true ;
}
};
const clipCache = new Map ();
function getClip (name ) {
if (!clipCache.has (name)) {
clipCache.set (name, loadClip (name));
}
return clipCache.get (name);
}
See Also
threejs-loaders - Loading animated GLTF models
threejs-fundamentals - Clock and animation loop
threejs-shaders - Vertex animation in shaders
Connections
Domain: [[Design & Kreativitaet]]
Kategorie: [[3D & Animation]]
Navigation: [[Skills Uebersicht]], [[Home]]