aframe Components


Introduction

In the entity-component-system pattern, a component is a reusable and modular chunk of data that we plug into an entity to add appearance, behavior, and/or functionality.

In A-Frame, components modify entities which are 3D objects in the scene. We mix and compose components together to build complex objects. They let us encapsulate three.js and JavaScript code into modules that we can use declaratively from HTML. Components are roughly analogous to CSS.

Remarks

Definition Lifecycle Handler Methods

With the schema being the anatomy, the lifecycle methods are the physiology; the schema defines the shape of the data, the lifecycle handler methods use the data to modify the entity. The handlers will usually interact with the Entity API.

Overview of Methods

MethodDescription
initCalled once when the component is initialized. Used to set up initial state and instantiate variables.
updateCalled both when the component is initialized and whenever any of the component's properties is updated (e.g, via setAttribute). Used to modify the entity.
removeCalled when the component is removed from the entity (e.g., via removeAttribute) or when the entity is detached from the scene. Used to undo all previous modifications to the entity.
tickCalled on each render loop or tick of the scene. Used for continuous changes or checks.
playCalled whenever the scene or entity plays to add any background or dynamic behavior. Also called once when the component is initialized. Used to start or resume behavior.
pauseCalled whenever the scene or entity pauses to remove any background or dynamic behavior. Also called when the component is removed from the entity or when the entity is detached from the scene. Used to pause behavior.
updateSchemaCalled whenever any of the component's properties is updated. Can be used to dynamically modify the schema.

Component Prototype Properties

Within the methods, we have access to the component prototype via this:

PropertyDescription
this.dataParsed component properties computed from the schema default values, mixins, and the entity's attributes.
this.elReference to the [entity][entity] as an HTML element.
this.el.sceneElReference to the [scene][scene] as an HTML element.
this.idIf the component can have [multiple instances][multiple], the ID of the individual instance of the component (e.g., foo from sound__foo).

METHODS

.init ()

.init () is called once at the beginning of the component's lifecycle. An entity can call the component's init handler:

  • When the component is statically set on the entity in the HTML file and the page is loaded.
  • When the component is set on an attached entity via setAttribute.
  • When the component is set on an unattached entity, and the entity is then attached to the scene via appendChild.

The init handler is often used to:

  • Set up initial state and variables
  • Bind methods
  • Attach event listeners

For example, a cursor component's init would set state variables, bind methods, and add event listeners:

AFRAME.registerComponent('cursor', {
  // ...
  init: function () {
    // Set up initial state and variables.
    this.intersection = null;
    // Bind methods.
    this.onIntersection = AFRAME.utils.bind(this.onIntersection, this);
    // Attach event listener.
    this.el.addEventListener('raycaster-intersection', this.onIntersection);
  }
  // ...
});

.update (oldData)

.update (oldData) is called whenever the component's properties change, including at the beginning of the component's lifecycle. An entity can call a component's update handler:

  • After init () is called, at the beginning of component's lifecycle.
  • When the component's properties are updated with .setAttribute.

The update handler is often used to:

  • Do most of the work in making modifications to the entity, using this.data.
  • Modify the entity whenever one or more component properties change.

Granular modifications to the entity can be done by [diffing][diff] the current dataset (this.data) with the previous dataset before the update (oldData).

A-Frame calls .update() both at the beginning of a component's lifecycle and every time a component's data changes (e.g., as a result of setAttribute). The update handler often uses this.data to modify the entity. The update handler has access to the previous state of a component's data via its first argument. We can use the previous data of a component to tell exactly which properties changed to do granular updates.

For example, the visible component's update sets the visibility of the entity.

AFRAME.registerComponent('visible', {
  /**
   * this.el is the entity element.
   * this.el.object3D is the three.js object of the entity.
   * this.data is the component's property or properties.
   */
  update: function (oldData) {
    this.el.object3D.visible = this.data;
  }
  // ...
});

.remove ()

.remove () is called whenever the component is detached from the entity. An entity can call a component's remove handler:

  • When the component is removed from the entity via removeAttribute.
  • When the entity is detached from the scene (e.g., removeChild).

The remove handler is often used to:

  • Remove, undo, or clean up all of the component's modifications to the entity.
  • Detach event listeners.

For example, when the [light component][light] is removed, the light component will remove the light object that it had previously set on the entity, thus removing it from the scene.

AFRAME.registerComponent('light', {
  // ...
  remove: function () {
    this.el.removeObject3D('light');
  }
  // ...
});

.tick (time, timeDelta)

.tick () is called on each tick or frame of the scene's render loop. The scene will call a component's tick handler:

  • On each frame of the render loop.
  • On the order of 60 to 120 times per second.
  • If the entity or scene is not paused (e.g., the Inspector is open).
  • If the entity is still attached to the scene.

The tick handler is often used to:

  • Continuously modify the entity on each frame or on an interval.
  • Poll for conditions.

The tick handler is provided the global uptime of the scene in milliseconds (time) and the time difference in milliseconds since the last frame (timeDelta). These can be used for interpolation or to only run parts of the tick handler on a set interval.

For example, the tracked controls component will progress the controller's animations, update the controller's position and rotation, and check for button presses.

AFRAME.registerComponent('tracked-controls', {
  // ...
  tick: function (time, timeDelta) {
    this.updateMeshAnimation();
    this.updatePose();
    this.updateButtons();
  }
  // ...
});

.pause ()

.pause () is called when the entity or scene pauses. The entity can call a component's pause handler:

  • Before the component is removed, before the remove handler is called.
  • When the entity is paused with Entity.pause ().
  • When the scene is paused with Scene.pause () (e.g., the Inspector is opened).

The pause handler is often used to:

  • Remove event listeners.
  • Remove any chances of dynamic behavior.

For example, the sound component will pause the sound and remove an event listener that would have played a sound on an event:

AFRAME.registerComponent('sound', {
  // ...
  pause: function () {
    this.pauseSound();
    this.removeEventListener();
  }
  // ...
});

.play ()

.play () is called when the entity or scene resumes. The entity can call a component's play handler:

  • When the component is first attached, after the update handler is called.
  • When the entity was paused but then resumed with Entity.play ().
  • When the scene was paused but then resumed with Scene.play ().

The play handler is often use to:

  • Add event listeners.

For example, the sound component will play the sound and update the event listener that would play a sound on an event:

AFRAME.registerComponent('sound', {
  // ...
  play: function () {
    if (this.data.autoplay) { this.playSound(); }
    this.updateEventListener();
  }
  // ...
});

.updateSchema (data)

.updateSchema (), if defined, is called on every update in order to check if the schema needs to be dynamically modified.

The updateSchema handler is often used to:

  • Dynamically update or extend the schema, usually depending on the value of a property.

For example, the geometry component checks if the primitive property changed to determine whether to update the schema for a different type of geometry:

AFRAME.registerComponent('geometry', {
  // ...
  updateSchema: (newData) {
    if (newData.primitive !== this.data.primitive) {
      this.extendSchema(GEOMETRIES[newData.primitive].schema);
    }
  }
  // ...
});

COMPONENT PROTOTYPE METHODS

.flushToDOM ()

To save on CPU time on stringification, A-Frame will only update in debug mode the component’s serialized representation in the actual DOM. Calling flushToDOM () will manually serialize the component’s data and update the DOM:

document.querySelector('[geometry]').components.geometry.flushToDOM();