Пользовательские события: emit и addEventListener

В A-Frame объекты сцены (entities) поддерживают систему событий, которая позволяет создавать интерактивные и динамические взаимодействия между компонентами. Основными инструментами для работы с пользовательскими событиями являются методы emit и addEventListener.


Метод emit

Метод emit используется для генерации собственного события на объекте. Его синтаксис:

entity.emit(eventName, detail, bubble);
  • eventName – строка, имя события.
  • detail – объект с дополнительными данными события (необязательный).
  • bubble – булево значение, определяющее, будет ли событие всплывать по иерархии сцены (по умолчанию true).

Пример генерации события:

let box = document.querySelector('#box');
box.emit('jump', { height: 2 });

В данном примере объект с id box генерирует событие jump с дополнительным параметром height.

Особенности emit:

  • Событие можно генерировать на любом объекте сцены.
  • Можно передавать произвольные данные через detail.
  • Всплытие позволяет реагировать на событие родительским объектам.

Метод addEventListener

Метод addEventListener привязывает обработчик к событию на объекте:

entity.addEventListener(eventName, callback);
  • eventName – строка, имя события.
  • callback – функция, которая будет вызвана при срабатывании события. Объект события доступен как параметр функции.

Пример использования:

let box = document.querySelector('#box');

box.addEventListener('jump', function(evt) {
    console.log('Высота прыжка:', evt.detail.height);
});

При вызове emit с событием jump функция обработчика получит объект события с полем detail, содержащим переданные данные.

Особенности addEventListener:

  • Поддерживает все стандартные события DOM, а также пользовательские события A-Frame.
  • Можно регистрировать несколько обработчиков для одного события.
  • Обработчик может отменить стандартное поведение с помощью evt.preventDefault().

Применение пользовательских событий в компонентах

Компоненты A-Frame могут как генерировать, так и слушать события, что позволяет строить модульные и переиспользуемые решения.

Пример создания компонента с пользовательским событием:

AFRAME.registerComponent('jumper', {
    schema: { height: { type: 'number', default: 1 } },

    init: function () {
        this.el.addEventListener('click', () => {
            this.el.emit('jump', { height: this.data.height });
        });
    }
});

Здесь компонент jumper при клике на объект вызывает событие jump с параметром height. Другой компонент или сам объект может слушать это событие:

AFRAME.registerComponent('logger', {
    init: function () {
        this.el.addEventListener('jump', (evt) => {
            console.log('Объект прыгнул на высоту', evt.detail.height);
        });
    }
});

Такое разделение логики обеспечивает чистую архитектуру и упрощает масштабирование сцены.


Всплытие и делегирование событий

События в A-Frame могут всплывать по иерархии сцены, аналогично DOM-событиям. Это позволяет родительским элементам реагировать на действия дочерних объектов:

let scene = document.querySelector('a-scene');
scene.addEventListener('jump', (evt) => {
    console.log('Событие сработало на дочернем объекте:', evt.target.id);
});
  • evt.target – объект, который изначально вызвал событие.
  • evt.currentTarget – объект, на котором обработчик был зарегистрирован.

Всплытие можно отключить, установив третий параметр bubble в emit:

box.emit('jump', { height: 2 }, false);

Теперь событие не будет распространяться на родителей.


Передача данных через события

Поле detail в объекте события позволяет передавать любые данные. Это может быть числовое значение, объект с несколькими свойствами или даже функции:

box.emit('customEvent', {
    message: 'Привет',
    coords: { x: 1, y: 2, z: 3 },
    callback: () => console.log('Вызов из события')
});

При обработке события данные можно использовать напрямую:

box.addEventListener('customEvent', (evt) => {
    console.log(evt.detail.message);
    evt.detail.callback();
});

Такой подход делает компоненты A-Frame гибкими и взаимосвязанными без жесткой зависимости между ними.


Практические советы

  • Пользовательские события удобны для интерактивных элементов, триггеров анимации, взаимодействий между компонентами.
  • Для сложных сцен рекомендуется разделять генерацию и обработку событий на разные компоненты.
  • Следует использовать всплытие только при необходимости, чтобы избежать случайных срабатываний в больших иерархиях объектов.
  • Для отладки событий полезно логировать evt.type и evt.detail, чтобы понимать, какие данные передаются.

Использование emit и addEventListener в A-Frame создаёт мощный механизм взаимодействия между объектами, позволяя строить сцены с динамическим поведением и высокой степенью модульности.