0
# Animation System
1
2
Three.js provides a comprehensive keyframe-based animation system for animating object properties over time. The animation system supports timeline control, blending, and complex animation sequencing with multiple tracks and interpolation modes.
3
4
## Capabilities
5
6
### Animation Mixer
7
8
Central animation control system that manages animation playback for objects and their hierarchies.
9
10
```javascript { .api }
11
/**
12
* Animation mixer for controlling multiple animation clips on objects
13
*/
14
class AnimationMixer extends EventDispatcher {
15
/**
16
* Create animation mixer for specified root object
17
* @param root - Root object that will be animated
18
*/
19
constructor(root: Object3D);
20
21
/** Root object being animated */
22
root: Object3D;
23
24
/** Current time in seconds */
25
time: number;
26
27
/** Time scale multiplier for all animations */
28
timeScale: number;
29
30
/**
31
* Create action for animation clip
32
* @param clip - Animation clip to create action for
33
* @param optionalRoot - Optional specific root for this action
34
* @returns Animation action ready for playback control
35
*/
36
clipAction(clip: AnimationClip, optionalRoot?: Object3D): AnimationAction;
37
38
/**
39
* Create action for animation clip by name
40
* @param name - Name of animation clip to find
41
* @param optionalRoot - Optional specific root for this action
42
* @returns Animation action or null if not found
43
*/
44
clipActionByName(name: string, optionalRoot?: Object3D): AnimationAction | null;
45
46
/**
47
* Get existing action for animation clip
48
* @param clip - Animation clip to find action for
49
* @param optionalRoot - Optional specific root for this action
50
* @returns Existing animation action or null
51
*/
52
existingAction(clip: AnimationClip, optionalRoot?: Object3D): AnimationAction | null;
53
54
/**
55
* Stop all currently active actions
56
* @returns This mixer for chaining
57
*/
58
stopAllAction(): AnimationMixer;
59
60
/**
61
* Update animations by specified time delta
62
* @param deltaTimeInSeconds - Time elapsed since last update in seconds
63
* @returns This mixer for chaining
64
*/
65
update(deltaTimeInSeconds: number): AnimationMixer;
66
67
/**
68
* Set time for all actions directly
69
* @param timeInSeconds - Time to set in seconds
70
* @returns This mixer for chaining
71
*/
72
setTime(timeInSeconds: number): AnimationMixer;
73
74
/**
75
* Get list of all actions on root or descendant objects
76
* @returns Array of all animation actions
77
*/
78
getRoot(): Object3D;
79
80
/**
81
* Deallocate all memory used by this mixer
82
*/
83
uncacheClip(clip: AnimationClip): void;
84
85
/**
86
* Deallocate memory used for root object
87
*/
88
uncacheRoot(root: Object3D): void;
89
90
/**
91
* Deallocate memory used for action
92
*/
93
uncacheAction(clip: AnimationClip, optionalRoot?: Object3D): void;
94
}
95
```
96
97
**Usage Example:**
98
99
```javascript
100
import { AnimationMixer, Clock } from 'three';
101
102
// Create mixer for loaded model
103
const mixer = new AnimationMixer(gltfScene);
104
105
// Create actions for loaded animations
106
const walkAction = mixer.clipAction(walkClip);
107
const runAction = mixer.clipAction(runClip);
108
109
// Configure and play animations
110
walkAction.play();
111
runAction.setWeight(0); // Start with walk only
112
113
// Animation loop
114
const clock = new Clock();
115
function animate() {
116
const deltaTime = clock.getDelta();
117
mixer.update(deltaTime);
118
119
requestAnimationFrame(animate);
120
renderer.render(scene, camera);
121
}
122
```
123
124
### Animation Clips
125
126
Container for animation data defining property changes over time using keyframe tracks.
127
128
```javascript { .api }
129
/**
130
* Container for keyframe track data representing an animation sequence
131
*/
132
class AnimationClip {
133
/**
134
* Create animation clip from keyframe tracks
135
* @param name - Name identifier for this clip
136
* @param duration - Length of animation in seconds (-1 for auto-calculate)
137
* @param tracks - Array of keyframe tracks defining property animations
138
* @param blendMode - How this clip blends with others
139
*/
140
constructor(name?: string, duration?: number, tracks?: KeyframeTrack[], blendMode?: AnimationBlendMode);
141
142
/** Unique name for this animation clip */
143
name: string;
144
145
/** Duration of animation in seconds */
146
duration: number;
147
148
/** Array of keyframe tracks defining property animations */
149
tracks: KeyframeTrack[];
150
151
/** Blend mode for combining with other animations */
152
blendMode: AnimationBlendMode;
153
154
/**
155
* Create copy of this animation clip
156
* @returns New animation clip with copied data
157
*/
158
clone(): AnimationClip;
159
160
/**
161
* Optimize clip by removing unnecessary keyframes
162
* @returns This clip for chaining
163
*/
164
optimize(): AnimationClip;
165
166
/**
167
* Reset duration based on track lengths
168
* @returns This clip for chaining
169
*/
170
resetDuration(): AnimationClip;
171
172
/**
173
* Trim clip to specified time range
174
* @param startTime - Start time in seconds
175
* @param endTime - End time in seconds
176
* @returns This clip for chaining
177
*/
178
trim(): AnimationClip;
179
180
/**
181
* Validate all tracks in this clip
182
* @returns True if valid
183
*/
184
validate(): boolean;
185
}
186
187
// Animation blend modes
188
type AnimationBlendMode =
189
| typeof NormalAnimationBlendMode // 2500 - Replace existing values
190
| typeof AdditiveAnimationBlendMode; // 2501 - Add to existing values
191
```
192
193
**Usage Example:**
194
195
```javascript
196
import { AnimationClip, VectorKeyframeTrack, NumberKeyframeTrack } from 'three';
197
198
// Create keyframe tracks
199
const positionTrack = new VectorKeyframeTrack(
200
'.position', // Property path
201
[0, 1, 2], // Times
202
[0, 0, 0, 10, 0, 0, 0, 0, 0] // Values (x,y,z for each time)
203
);
204
205
const opacityTrack = new NumberKeyframeTrack(
206
'.material.opacity', // Property path
207
[0, 1, 2], // Times
208
[1, 0.5, 1] // Values
209
);
210
211
// Create animation clip
212
const fadeAndMove = new AnimationClip('fadeAndMove', 2, [
213
positionTrack,
214
opacityTrack
215
]);
216
```
217
218
### Animation Actions
219
220
Playback control interface for animation clips with timing, weights, and state management.
221
222
```javascript { .api }
223
/**
224
* Controls playback of an animation clip with timing and blending options
225
*/
226
class AnimationAction {
227
/** Animation clip being controlled */
228
clip: AnimationClip;
229
230
/** Animation mixer managing this action */
231
mixer: AnimationMixer;
232
233
/** Current playback time in seconds */
234
time: number;
235
236
/** Playback speed multiplier */
237
timeScale: number;
238
239
/** Influence weight for blending (0-1) */
240
weight: number;
241
242
/** Number of repetitions (Infinity for endless) */
243
repetitions: number;
244
245
/** Whether action is currently paused */
246
paused: boolean;
247
248
/** Whether action is currently enabled */
249
enabled: boolean;
250
251
/** Whether to clamp when animation reaches end */
252
clampWhenFinished: boolean;
253
254
/** Whether to automatically start when mixed */
255
zeroSlopeAtStart: boolean;
256
257
/** Whether to smoothly end interpolation */
258
zeroSlopeAtEnd: boolean;
259
260
/** Loop mode for animation playback */
261
loop: AnimationActionLoopStyles;
262
263
/**
264
* Start or resume playback
265
* @returns This action for chaining
266
*/
267
play(): AnimationAction;
268
269
/**
270
* Stop playback and reset to beginning
271
* @returns This action for chaining
272
*/
273
stop(): AnimationAction;
274
275
/**
276
* Pause playback at current time
277
* @returns This action for chaining
278
*/
279
pause(): AnimationAction;
280
281
/**
282
* Pause all other actions and play this one
283
* @returns This action for chaining
284
*/
285
stopFading(): AnimationAction;
286
287
/**
288
* Reset action to initial state
289
* @returns This action for chaining
290
*/
291
reset(): AnimationAction;
292
293
/**
294
* Check if action is currently running
295
* @returns True if action is playing
296
*/
297
isRunning(): boolean;
298
299
/**
300
* Check if action is scheduled to start
301
* @returns True if action is scheduled
302
*/
303
isScheduled(): boolean;
304
305
/**
306
* Set effective weight with fade in/out
307
* @param weight - Target weight (0-1)
308
* @param duration - Fade duration in seconds
309
* @returns This action for chaining
310
*/
311
fadeIn(duration: number): AnimationAction;
312
313
/**
314
* Fade out action weight to zero
315
* @param duration - Fade duration in seconds
316
* @returns This action for chaining
317
*/
318
fadeOut(duration: number): AnimationAction;
319
320
/**
321
* Cross-fade to another action
322
* @param fadeOutAction - Action to fade out
323
* @param duration - Cross-fade duration in seconds
324
* @param warp - Whether to synchronize durations
325
* @returns This action for chaining
326
*/
327
crossFadeFrom(fadeOutAction: AnimationAction, duration: number, warp?: boolean): AnimationAction;
328
329
/**
330
* Cross-fade from this action to another
331
* @param fadeInAction - Action to fade in
332
* @param duration - Cross-fade duration in seconds
333
* @param warp - Whether to synchronize durations
334
* @returns This action for chaining
335
*/
336
crossFadeTo(fadeInAction: AnimationAction, duration: number, warp?: boolean): AnimationAction;
337
338
/**
339
* Gradually change playback speed
340
* @param timeScale - Target time scale
341
* @param duration - Warp duration in seconds
342
* @returns This action for chaining
343
*/
344
warp(startTimeScale: number, endTimeScale: number, duration: number): AnimationAction;
345
346
/**
347
* Set loop mode and repetition count
348
* @param mode - Loop style constant
349
* @param repetitions - Number of loops (Infinity for endless)
350
* @returns This action for chaining
351
*/
352
setLoop(mode: AnimationActionLoopStyles, repetitions: number): AnimationAction;
353
354
/**
355
* Set effective weight immediately
356
* @param weight - Weight value (0-1)
357
* @returns This action for chaining
358
*/
359
setEffectiveWeight(weight: number): AnimationAction;
360
361
/**
362
* Get current effective weight
363
* @returns Current effective weight
364
*/
365
getEffectiveWeight(): number;
366
367
/**
368
* Set effective time scale immediately
369
* @param timeScale - Time scale multiplier
370
* @returns This action for chaining
371
*/
372
setEffectiveTimeScale(timeScale: number): AnimationAction;
373
374
/**
375
* Get current effective time scale
376
* @returns Current effective time scale
377
*/
378
getEffectiveTimeScale(): number;
379
380
/**
381
* Get total duration accounting for time scale and repetitions
382
* @returns Total duration in seconds
383
*/
384
getClip(): AnimationClip;
385
386
/**
387
* Get animation mixer controlling this action
388
* @returns Animation mixer instance
389
*/
390
getMixer(): AnimationMixer;
391
392
/**
393
* Get root object being animated
394
* @returns Root object
395
*/
396
getRoot(): Object3D;
397
}
398
399
// Loop style constants
400
type AnimationActionLoopStyles =
401
| typeof LoopOnce // 2200 - Play once and stop
402
| typeof LoopRepeat // 2201 - Repeat from beginning
403
| typeof LoopPingPong; // 2202 - Reverse direction each loop
404
```
405
406
### Keyframe Tracks
407
408
Base classes for defining property animations over time with various interpolation modes.
409
410
```javascript { .api }
411
/**
412
* Base class for keyframe-based property animation tracks
413
*/
414
abstract class KeyframeTrack {
415
/**
416
* Create keyframe track for animating property
417
* @param name - Property path (e.g. '.position.x', '.rotation')
418
* @param times - Array of time values in seconds
419
* @param values - Array of property values at each time
420
* @param interpolation - Interpolation mode between keyframes
421
*/
422
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<any>, interpolation?: InterpolationModes);
423
424
/** Property path being animated */
425
name: string;
426
427
/** Time values for keyframes */
428
times: Float32Array;
429
430
/** Property values at each keyframe */
431
values: Float32Array;
432
433
/** Type name for this track */
434
ValueTypeName: string;
435
436
/** Time interpolation mode */
437
interpolation: InterpolationModes;
438
439
/**
440
* Get interpolated value at specified time
441
* @param time - Time in seconds
442
* @returns Interpolated value
443
*/
444
evaluate(time: number): any;
445
446
/**
447
* Create copy of this track
448
* @returns New keyframe track with copied data
449
*/
450
clone(): this;
451
452
/**
453
* Scale all time values
454
* @param timeScale - Scale factor for times
455
* @returns This track for chaining
456
*/
457
scale(timeScale: number): this;
458
459
/**
460
* Trim track to specified time range
461
* @param startTime - Start time in seconds
462
* @param endTime - End time in seconds
463
* @returns This track for chaining
464
*/
465
trim(startTime: number, endTime: number): this;
466
467
/**
468
* Shift all keyframe times by offset
469
* @param timeOffset - Time offset in seconds
470
* @returns This track for chaining
471
*/
472
shift(timeOffset: number): this;
473
474
/**
475
* Optimize track by removing redundant keyframes
476
* @returns This track for chaining
477
*/
478
optimize(): this;
479
480
/**
481
* Validate track data consistency
482
* @returns True if track is valid
483
*/
484
validate(): boolean;
485
}
486
487
/**
488
* Keyframe track for animating Vector3 properties
489
*/
490
class VectorKeyframeTrack extends KeyframeTrack {
491
/**
492
* Create vector keyframe track
493
* @param name - Property path (e.g. '.position', '.scale')
494
* @param times - Array of time values
495
* @param values - Array of vector values [x,y,z,x,y,z,...]
496
* @param interpolation - Interpolation mode
497
*/
498
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<number>, interpolation?: InterpolationModes);
499
}
500
501
/**
502
* Keyframe track for animating number properties
503
*/
504
class NumberKeyframeTrack extends KeyframeTrack {
505
/**
506
* Create number keyframe track
507
* @param name - Property path (e.g. '.opacity', '.intensity')
508
* @param times - Array of time values
509
* @param values - Array of number values
510
* @param interpolation - Interpolation mode
511
*/
512
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<number>, interpolation?: InterpolationModes);
513
}
514
515
/**
516
* Keyframe track for animating Quaternion rotations
517
*/
518
class QuaternionKeyframeTrack extends KeyframeTrack {
519
/**
520
* Create quaternion keyframe track
521
* @param name - Property path (e.g. '.quaternion')
522
* @param times - Array of time values
523
* @param values - Array of quaternion values [x,y,z,w,x,y,z,w,...]
524
* @param interpolation - Interpolation mode
525
*/
526
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<number>, interpolation?: InterpolationModes);
527
}
528
529
/**
530
* Keyframe track for animating Color properties
531
*/
532
class ColorKeyframeTrack extends KeyframeTrack {
533
/**
534
* Create color keyframe track
535
* @param name - Property path (e.g. '.color', '.emissive')
536
* @param times - Array of time values
537
* @param values - Array of color values [r,g,b,r,g,b,...]
538
* @param interpolation - Interpolation mode
539
*/
540
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<number>, interpolation?: InterpolationModes);
541
}
542
543
/**
544
* Keyframe track for animating string/discrete properties
545
*/
546
class StringKeyframeTrack extends KeyframeTrack {
547
/**
548
* Create string keyframe track for discrete values
549
* @param name - Property path
550
* @param times - Array of time values
551
* @param values - Array of string values
552
*/
553
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<string>);
554
}
555
556
/**
557
* Keyframe track for animating boolean properties
558
*/
559
class BooleanKeyframeTrack extends KeyframeTrack {
560
/**
561
* Create boolean keyframe track
562
* @param name - Property path
563
* @param times - Array of time values
564
* @param values - Array of boolean values
565
*/
566
constructor(name: string, times: ArrayLike<number>, values: ArrayLike<boolean>);
567
}
568
569
// Interpolation modes
570
type InterpolationModes =
571
| typeof InterpolateDiscrete // Step interpolation
572
| typeof InterpolateLinear // Linear interpolation
573
| typeof InterpolateSmooth; // Smooth/cubic interpolation
574
```
575
576
### Animation Utilities
577
578
Utility functions for creating, modifying, and managing animation data.
579
580
```javascript { .api }
581
/**
582
* Utility functions for animation system
583
*/
584
class AnimationUtils {
585
/**
586
* Convert track array to hierarchy of clips
587
* @param tracks - Array of keyframe tracks
588
* @returns Array of animation clips
589
*/
590
static arraySlice(array: any[], from: number, to: number): any[];
591
592
/**
593
* Create clip from morph target animation
594
* @param name - Animation name
595
* @param morphTargetNames - Names of morph targets
596
* @param fps - Frames per second
597
* @returns Animation clip for morph targets
598
*/
599
static makeSameType(source: KeyframeTrack, times: ArrayLike<number>, values: ArrayLike<any>): KeyframeTrack;
600
601
/**
602
* Get keyframe track constructor by name
603
* @param typeName - Track type name
604
* @returns Track constructor function
605
*/
606
static getKeyframeOrder(times: ArrayLike<number>): number[];
607
608
/**
609
* Sort keyframes by time and remove duplicates
610
* @param track - Keyframe track to sort
611
* @returns Sorted track
612
*/
613
static sortedArray(values: any[], stride: number, order: number[]): any[];
614
615
/**
616
* Create animation clip from object hierarchy
617
* @param root - Root object to extract animation from
618
* @param name - Name for created clip
619
* @param fps - Target framerate
620
* @returns Animation clip extracted from object
621
*/
622
static flattenJSON(jsonKeys: string[], times: number[], values: any[], valuePropertyName: string): KeyframeTrack[];
623
624
/**
625
* Utility to sub-sample keyframe track
626
* @param track - Source track
627
* @param newTimes - New time values
628
* @returns Sub-sampled track
629
*/
630
static subclip(sourceClip: AnimationClip, name: string, startFrame: number, endFrame: number, fps?: number): AnimationClip;
631
}
632
```
633
634
### Animation Object Groups
635
636
System for managing animations across multiple objects sharing similar structure.
637
638
```javascript { .api }
639
/**
640
* Group of objects that can share animation data and playback timing
641
*/
642
class AnimationObjectGroup {
643
/**
644
* Create animation object group
645
* @param objects - Objects to group together
646
*/
647
constructor(...objects: Object3D[]);
648
649
/** UUID for this group */
650
uuid: string;
651
652
/** Statistics about group usage */
653
stats: {
654
bindingsPerObject: number;
655
objects: {
656
total: number;
657
inUse: number;
658
};
659
};
660
661
/**
662
* Add objects to this group
663
* @param objects - Objects to add
664
*/
665
add(...objects: Object3D[]): void;
666
667
/**
668
* Remove objects from group
669
* @param objects - Objects to remove
670
*/
671
remove(...objects: Object3D[]): void;
672
673
/**
674
* Remove all objects from group
675
*/
676
uncache(...objects: Object3D[]): void;
677
}
678
```
679
680
### Property Animation System
681
682
Low-level animation system components for binding tracks to object properties and mixing multiple values.
683
684
```javascript { .api }
685
/**
686
* Binds animation tracks to object properties using property paths
687
*/
688
class PropertyBinding {
689
/**
690
* Create property binding for animation track
691
* @param rootNode - Root object containing the property
692
* @param path - Property path (e.g., '.position', '.rotation.x', '.material.opacity')
693
* @param parsedPath - Pre-parsed property path (internal use)
694
*/
695
constructor(rootNode: Object3D, path: string, parsedPath?: any);
696
697
/** Root object containing the property */
698
rootNode: Object3D;
699
700
/** Property path string */
701
path: string;
702
703
/** Parsed property path components */
704
parsedPath: any;
705
706
/** Target object containing the property */
707
node: any;
708
709
/** Final property being animated */
710
nodeProperty: any;
711
712
/**
713
* Get current property value
714
* @param targetArray - Array to store result in
715
*/
716
getValue(targetArray: ArrayLike<number>): void;
717
718
/**
719
* Set property value from array
720
* @param sourceArray - Array containing new values
721
*/
722
setValue(sourceArray: ArrayLike<number>): void;
723
724
/**
725
* Bind property for animation
726
*/
727
bind(): void;
728
729
/**
730
* Unbind property
731
*/
732
unbind(): void;
733
734
/**
735
* Create property binding from animation track
736
* @param root - Root object
737
* @param track - Keyframe track to bind
738
* @returns Property binding instance
739
*/
740
static create(root: Object3D, track: KeyframeTrack): PropertyBinding;
741
742
/**
743
* Parse property path into components
744
* @param path - Property path string
745
* @returns Parsed path object
746
*/
747
static parseTrackName(path: string): any;
748
749
/**
750
* Find property node in object hierarchy
751
* @param root - Root object to search from
752
* @param nodeName - Name of node to find
753
* @returns Found object or null
754
*/
755
static findNode(root: Object3D, nodeName: string): Object3D | null;
756
}
757
758
/**
759
* Mixes multiple property values for animation blending
760
*/
761
class PropertyMixer {
762
/**
763
* Create property mixer for blending multiple values
764
* @param binding - Property binding to mix values for
765
* @param typeName - Type name for the property values
766
* @param valueSize - Number of components per value
767
*/
768
constructor(binding: PropertyBinding, typeName: string, valueSize: number);
769
770
/** Property binding being mixed */
771
binding: PropertyBinding;
772
773
/** Type name for values */
774
typeName: string;
775
776
/** Number of components per value */
777
valueSize: number;
778
779
/** Buffer for accumulating mixed values */
780
buffer: ArrayLike<number>;
781
782
/** Current cumulative weight */
783
cumulativeWeight: number;
784
785
/** Whether cumulative weight was used */
786
cumulativeWeightAdditive: number;
787
788
/** Whether binding has been applied */
789
useCount: number;
790
791
/** Reference count for this mixer */
792
referenceCount: number;
793
794
/**
795
* Accumulate weighted value into buffer
796
* @param accuIndex - Index in accumulator
797
* @param weight - Blend weight
798
* @param values - Source values array
799
* @param valueOffset - Offset in values array
800
*/
801
accumulate(accuIndex: number, weight: number, values: ArrayLike<number>, valueOffset: number): void;
802
803
/**
804
* Accumulate additive value into buffer
805
* @param accuIndex - Index in accumulator
806
* @param weight - Blend weight
807
* @param values - Source values array
808
* @param valueOffset - Offset in values array
809
*/
810
accumulateAdditive(accuIndex: number, weight: number, values: ArrayLike<number>, valueOffset: number): void;
811
812
/**
813
* Apply accumulated values to property
814
* @param accuIndex - Index in accumulator
815
*/
816
apply(accuIndex: number): void;
817
818
/**
819
* Save current property state
820
* @param accuIndex - Index to save at
821
*/
822
saveOriginalState(accuIndex: number): void;
823
824
/**
825
* Restore saved property state
826
* @param accuIndex - Index to restore from
827
*/
828
restoreOriginalState(accuIndex: number): void;
829
}
830
```
831
832
## Types
833
834
```javascript { .api }
835
// Animation-specific type definitions
836
type AnimationBlendMode = number;
837
type AnimationActionLoopStyles = number;
838
type InterpolationModes = number;
839
840
// Loop constants
841
declare const LoopOnce: 2200;
842
declare const LoopRepeat: 2201;
843
declare const LoopPingPong: 2202;
844
845
// Blend mode constants
846
declare const NormalAnimationBlendMode: 2500;
847
declare const AdditiveAnimationBlendMode: 2501;
848
849
// Interpolation constants
850
declare const InterpolateDiscrete: 2300;
851
declare const InterpolateLinear: 2301;
852
declare const InterpolateSmooth: 2302;
853
```
854
855
## Usage Examples
856
857
**Complex Animation Setup:**
858
859
```javascript
860
import * as THREE from 'three';
861
862
// Create mixer for character
863
const mixer = new THREE.AnimationMixer(character);
864
865
// Load multiple animation clips
866
const idleClip = character.animations.find(clip => clip.name === 'idle');
867
const walkClip = character.animations.find(clip => clip.name === 'walk');
868
const runClip = character.animations.find(clip => clip.name === 'run');
869
870
// Create actions
871
const idleAction = mixer.clipAction(idleClip);
872
const walkAction = mixer.clipAction(walkClip);
873
const runAction = mixer.clipAction(runClip);
874
875
// Configure actions
876
idleAction.play();
877
walkAction.setWeight(0).play();
878
runAction.setWeight(0).play();
879
880
// Animation state management
881
let currentAction = idleAction;
882
883
function transitionTo(newAction, duration = 0.5) {
884
if (currentAction !== newAction) {
885
currentAction.fadeOut(duration);
886
newAction.reset().fadeIn(duration).play();
887
currentAction = newAction;
888
}
889
}
890
891
// Update loop
892
const clock = new THREE.Clock();
893
function animate() {
894
const deltaTime = clock.getDelta();
895
mixer.update(deltaTime);
896
897
requestAnimationFrame(animate);
898
renderer.render(scene, camera);
899
}
900
```