В Three.js анимации реализованы через модульную систему, основанную на объектах AnimationClip, AnimationMixer и AnimationAction. Такая архитектура позволяет воспроизводить сложные движения моделей, управлять временными шкалами, смешивать анимации и динамически изменять их параметры.
Основная идея заключается в следующем:
Анимационные данные могут поступать из различных источников:
AnimationClip представляет собой набор ключевых кадров, описывающих изменение свойств объектов во времени. Он хранит саму анимацию, но не управляет её воспроизведением.
AnimationClip содержит:
Треки представляют собой временные последовательности изменений конкретных свойств объекта.
AnimationClip
├─ name
├─ duration
└─ tracks[]
├─ VectorKeyframeTrack
├─ QuaternionKeyframeTrack
├─ NumberKeyframeTrack
└─ ColorKeyframeTrack
Каждый KeyframeTrack содержит:
Клип можно создать программно.
const times = [0, 1, 2];
const values = [0, 5, 0];
const track = new THREE.NumberKeyframeTrack(
'.position[x]',
times,
values
);
const clip = new THREE.AnimationClip(
'moveX',
2,
[track]
);
Здесь:
times — временные точкиvalues — значения свойства.position[x] — путь к анимируемому свойствуВ Three.js используются несколько типов треков.
Используется для анимации числовых значений.
opacity
intensity
position.x
Для трёхмерных векторов.
position
scale
direction
Для вращений.
Использование кватернионов позволяет избежать проблем gimbal lock.
rotation
quaternion
Для изменения цвета.
material.color
Ключевые кадры соединяются интерполяцией.
Доступны режимы:
Пример:
track.setInterpolation(THREE.InterpolateSmooth);
AnimationMixer отвечает за управление воспроизведением анимаций.
Он связан с конкретным объектом сцены.
const mixer = new THREE.AnimationMixer(mesh);
Mixer выполняет:
Обновление происходит в игровом цикле:
const delta = clock.getDelta();
mixer.update(delta);
AnimationAction — объект управления воспроизведением AnimationClip.
Каждый Action создаётся через AnimationMixer.
const action = mixer.clipAction(clip);
Action управляет:
| Свойство | Назначение |
|---|---|
| enabled | активность действия |
| paused | пауза |
| time | текущее время |
| timeScale | скорость |
| weight | влияние при смешивании |
| loop | режим цикла |
Запуск анимации.
action.play();
Полная остановка.
action.stop();
Сброс времени.
action.reset();
Плавное появление.
action.fadeIn(0.5);
Плавное исчезновение.
action.fadeOut(0.5);
AnimationAction поддерживает несколько режимов циклов.
Анимация проигрывается один раз.
action.setLoop(THREE.LoopOnce);
Повторяется заданное количество раз.
action.setLoop(THREE.LoopRepeat, 3);
Анимация воспроизводится вперёд и назад.
action.setLoop(THREE.LoopPingPong, 5);
Скорость регулируется параметром timeScale.
action.timeScale = 2;
Возможности:
| Значение | Эффект |
|---|---|
| 1 | нормальная скорость |
| 2 | в два раза быстрее |
| 0.5 | медленнее |
| -1 | воспроизведение назад |
Система blending позволяет плавно переходить между анимациями.
Пример:
walkAction.fadeOut(0.5);
runAction.fadeIn(0.5).play();
Таким образом создаётся переход:
Walk → Run
Каждая AnimationAction имеет параметр weight.
0 = нет влияния
1 = полное влияние
action.setEffectiveWeight(0.5);
Это позволяет комбинировать движения.
Crossfade — плавная смена анимации.
action1.crossFadeTo(action2, 0.3, false);
Параметры:
targetAction
duration
warp
Часто модели содержат несколько анимаций.
Пример с glTF:
loader.load('character.glb', (gltf) => {
const model = gltf.scene;
const mixer = new THREE.AnimationMixer(model);
const clips = gltf.animations;
const idle = mixer.clipAction(clips[0]);
const walk = mixer.clipAction(clips[1]);
const run = mixer.clipAction(clips[2]);
idle.play();
});
AnimationAction хранит собственное время.
action.time
Можно вручную задать позицию анимации.
action.time = 1.5;
После завершения анимации можно зафиксировать финальное состояние.
action.clampWhenFinished = true;
И отключить повтор:
action.setLoop(THREE.LoopOnce);
AnimationMixer поддерживает события.
Срабатывает после окончания клипа.
mixer.addEventListener('finished', (event) => {
console.log('Animation finished');
});
Срабатывает при повторе.
mixer.addEventListener('loop', (event) => {
});
const mixer = new THREE.AnimationMixer(model);
const idle = mixer.clipAction(clips[0]);
const walk = mixer.clipAction(clips[1]);
idle.play();
function switchToWalk(){
idle.fadeOut(0.3);
walk
.reset()
.fadeIn(0.3)
.play();
}
Большие сцены могут содержать десятки анимаций. Для оптимизации используются:
action.stop();
Чем меньше анимируемых свойств, тем быстрее вычисления.
Для персонажа или сложного объекта используется один mixer.
AnimationClip может генерироваться динамически.
Пример вращения:
const times = [0, 2];
const values = [
0,0,0,1,
0,1,0,0
];
const track = new THREE.QuaternionKeyframeTrack(
'.quaternion',
times,
values
);
const clip = new THREE.AnimationClip(
'rotate',
2,
[track]
);
В моделях персонажей AnimationClip управляет:
Чаще всего такие клипы импортируются из формата glTF.
Каждый трек содержит путь вида:
Bone.position
Bone.quaternion
Bone.scale
Разделение обязанностей делает систему гибкой.
AnimationClip
AnimationAction
Такой подход позволяет: