Синхронизация анимаций между сущностями

В 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.
  • Для сложных последовательностей — кастомные компоненты с tweening или собственная система управления анимациями.
  • Важно учитывать разницу между анимацией свойств (animation) и анимацией скелета (animation-mixer), так как методы синхронизации различаются.