В A-Frame анимации управляются с помощью компонента
animation и его расширений (animation__* для
нескольких анимаций). Для синхронизации нескольких сущностей важно
понимать ключевые параметры: property, from,
to, dur, delay, loop
и easing. Они определяют какое свойство
изменяется, от какого значения к какому,
за какой промежуток времени, с какой
задержкой, сколько раз повторяется и
как интерполируется изменение.
<a-entity id="box1" geometry="primitive: box" material="color: red"
animation="property: rotation; to: 0 360 0; dur: 2000; loop: true">
</a-entity>
<a-entity id="box2" geometry="primitive: box" material="color: blue"
animation="property: rotation; to: 0 360 0; dur: 2000; loop: true">
</a-entity>
В примере обе коробки вращаются синхронно, поскольку параметры
to, dur и loop совпадают.
Событийная модель A-Frame позволяет запускать анимации одной сущности по событию другой. Это особенно полезно для цепочек анимаций или реакций на пользовательское взаимодействие.
<a-entity id="sphere" geometry="primitive: sphere" material="color: yellow"></a-entity>
<a-entity id="cube" geometry="primitive: box" material="color: green"
animation__rotate="property: rotation; to: 0 360 0; dur: 1000; startEvents: rotateSphere">
</a-entity>
<script>
const sphere = document.querySelector('#sphere');
const cube = document.querySelector('#cube');
sphere.addEventListener('click', () => {
cube.emit('rotateSphere');
});
</script>
Здесь клик по сфере запускает вращение куба. Параметр
startEvents указывает имя события, которое инициирует
анимацию.
Для точной синхронизации нескольких сущностей можно использовать
delay в миллисекундах. Это позволяет выстраивать
каскадные или согласованные движения.
<a-entity id="box1" animation="property: position; to: 0 2 0; dur: 1000; delay: 0"></a-entity>
<a-entity id="box2" animation="property: position; to: 0 2 0; dur: 1000; delay: 500"></a-entity>
Box2 начнет движение через 500 мс после Box1. Для сложных сцен можно
динамически менять delay через JavaScript, создавая
ритмические и координированные анимации.
animation-mixerДля моделей в формате GLTF или GLB часто применяется компонент
animation-mixer. Он управляет анимациями скелетных
моделей и позволяет синхронизировать их по времени или
состоянию.
<a-entity gltf-model="#character1" animation-mixer="clip: Idle; loop: repeat"></a-entity>
<a-entity gltf-model="#character2" animation-mixer="clip: Idle; loop: repeat"></a-entity>
Чтобы синхронизировать несколько моделей, необходимо контролировать старт анимаций через события или таймеры:
const char1 = document.querySelector('#character1');
const char2 = document.querySelector('#character2');
char1.addEventListener('model-loaded', () => {
char1.components['animation-mixer'].playAction('Idle');
char2.components['animation-mixer'].playAction('Idle');
});
Все модели начинают анимацию одновременно, что обеспечивает точную синхронизацию.
tween и кастомных систем для синхронизацииДля более сложных сценариев применяется кастомный компонент с
tweening через библиотеки, такие как tween.js. Это
позволяет согласовывать несколько свойств одновременно
и управлять анимациями через центральный таймер.
AFRAME.registerComponent('sync-animation', {
init: function () {
const box1 = document.querySelector('#box1');
const box2 = document.querySelector('#box2');
const tween1 = new TWEEN.Tween({y: 0})
.to({y: 2}, 1000)
.onUpdate(obj => box1.object3D.position.y = obj.y)
.repeat(Infinity)
.yoyo(true)
.start();
const tween2 = new TWEEN.Tween({y: 0})
.to({y: 2}, 1000)
.delay(500)
.onUpdate(obj => box2.object3D.position.y = obj.y)
.repeat(Infinity)
.yoyo(true)
.start();
function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time);
}
animate();
}
});
Такой подход позволяет точно контролировать задержки,
длительности и порядок анимаций без зависимости от встроенного
animation компонента.
Создание собственной системы A-Frame для управления анимациями позволяет:
Пример системы:
AFRAME.registerSystem('animation-sync', {
init: function () {
this.animatedEntities = [];
},
registerEntity: function(entity, animationConfig) {
this.animatedEntities.push({entity, animationConfig});
},
playAll: function() {
this.animatedEntities.forEach(item => {
item.entity.setAttribute('animation', item.animationConfig);
});
}
});
В дальнейшем можно вызывать playAll для запуска всех
анимаций одновременно, что особенно полезно для сцен с множеством
объектов.
dur, loop и to у всех
сущностей.startEvents.animation-mixer + событие
model-loaded.animation) и анимацией скелета
(animation-mixer), так как методы синхронизации
различаются.