How to access the default camera from a component? - aframe

A component I'm making needs the default camera, but this.el.sceneEl.camera returns undefined.
Simplified example:
AFRAME.registerComponent('test', {
init: function () {
console.log(this.el.sceneEl.camera)
}
});
How can I retrieve the camera?

Might have to wait for the camera to be set? Need to document this, but there's an event:
this.el.sceneEl.addEventListener('camera-set-active', function (evt) {
console.log(evt.detail.cameraEl);
});

Related

meteor emit event then used by aframe eventlistener

I have an aframe object in main.html:
<a-gltf-model id='player1' foobox playmyclip src="#myMixBun" ></a-gltf-model>
I want to have it perform a gltf clip animation when the eventListener ‘driveplay’ is emitted.
In foofile.js:
AFRAME.registerComponent('playmyclip', {
init: function () {
var el = this.el;
el.addEventListener('driveplay', function () {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
});
}
});
Currently I have it so when the j key is hit ‘driveplay’ is emitted:
AFRAME.registerComponent('foobox', {
init: function() {
var el = this.el
var swingno = 0;
self = this;
document.addEventListener('keydown', (event) => {
const keyName = event.key;
if (keyName === 'j') {
el.emit('driveplay',{},true);
// code to store record of event in Mongo for second player
var playerid = self.el.getAttribute('id');
var playerMid = Games.findOne({name: playerid})._id;
Games.update({_id: playerMid},{$set:{swings : swingno}});
swingno = swingno + 1;
}
})
}
});
But I also need to have the animation to happen at the same time on player2‘s screen. So in the above code I increment a variable in Mongo every time the animation happens. Then in main.js meteor automatically emits an event whenever “swingno” changes in Mongo.
main.js:
import {EventEmitter} from 'meteor/raix:eventemitter';
Event = new EventEmitter();
var swingcnt1 = 0;
Template.hello.helpers({
counter() {
if (Games.findOne()) //mongo is ready to access
{
var plyr1Swing = Games.findOne({name: "player1"}).swings;
if (plyr1Swing !== swingcnt1) {
Event.emit('driveplay',{},true);
swingcnt1 = plyr1Swing;
console.log(“this shows on player2’s console automatically”, swingcnt1);
}
}
else {null}
return { ........};
},
When player1 hits the “j” key the animation happens correctly on his screen, plus the event is recorded in Mongo, and player2 receives the updated Mongo value (since it show in the console.log).
The problem is the
Event.emit('driveplay',{},true);
statement doesn’t trigger the animation in player2‘s screen. This is a little tricky, since I need meteor’s “raix:eventemitter” package to create an event that the aframe event listener can see. It’s possible I’m not actually emitting an event at all, since I don’t know how to test if it’s working. Or possibly aframe can’t see the emitted event.
Possibly there’s an easier way of doing this. Thanks for any help.
SOLVED The solution was to use the listener code from meteor’s raix:eventemitter package inside Aframe’s component.
https://atmospherejs.com/raix/eventemitter
AFRAME.registerComponent('playmyclip', {
init: function ()
var el = this.el;
listener = function() {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
};
Event.on('driveplay', listener);
}
});
Plus the foobox component no longer needs:
if (keyName === 'j') {
el.emit('driveplay',{},true);
Both player1 and player2 get the animation event from
Event.emit('driveplay',{},true);
in main.js when the meteor helper notices a change in the mongodb.

A-Frame : How to rotate a distant component with the raycaster?

In Aframe, is there a way to use the controller to rotate a distant component with the raycaster ?
Yes. cursor entity, with raycaster, and class='clickable' like this
<a-entity id="mouseCursor" cursor="rayOrigin: mouse" raycaster="objects:
.clickable"></a-entity>
then add a custom component on the object you want to spin, that listens for the mouse event, and then spins the object, until the mouse Leave event occurs. like this
AFRAME.registerComponent('over-listener', {
schema:{
mouseover:{type: 'boolean', default: false}
},
init: function () {
var el = this.el; // reference to the entity that contains this component
var data = this.data; // reference to the properties of this component.
// Listen for mouseenter event
this.el.addEventListener('mouseenter', function (evt) {
// You can't change the property directly. You must use setAttribute.
el.setAttribute('over-listener','mouseover', true);
// Change the color of the button to indicate rollover state is on.
el.setAttribute('material','color','#55ee00');;
});
// Listen for mouseleave event
this.el.addEventListener('mouseleave', function (evt) {
el.setAttribute('over-listener','mouseover', false);
el.setAttribute('material','color','orange');
});
},
tick: function(){ // called every frame
if(this.data.mouseover){ // Check the mouseover state
let elOcta = document.querySelector('#octahedron');
let rot = elOcta.getAttribute('rotation');
elOcta.setAttribute('rotation',{x: rot.x, y: rot.y , z: rot.z + 1});
}
}
});
here is a glitch
https://glitch.com/~rollover-rotate

How to listen to changes in the rotation of the camera in A-Frame?

Is there a way to add a listener on the current angle of view?
In other words, I'd like to trigger a function every time the user looks behind him.
The quickest way seems to be having a listener that checks the current head rotation and trust triggers the function if is within a certain range of degrees
Edit
The componentchange event is throttled. And it is more performant to not go through the event system for frequent updates. The camera rotation always changes every frame in VR so there is no need to think whether the camera has changed. So we read rotation every frame with component tick.
AFRAME.registerComponent('rotation-reader', {
tick: function () {
var rotation = this.el.getAttribute('rotation');
if (rotation.y < 180) {
// ...
}
}
});
// <a-camera rotation-reader>
Original
https://aframe.io/docs/0.2.0/core/entity.html#listening-for-component-changes
You can use the componentchanged event to listen to changes in the rotation:
document.querySelector('[camera]').addEventListener('componentchanged', function (evt) {
if (evt.name !== 'rotation') { return; }
if (evt.newData.y < 180) { // ... }
});
Or better as a component (this will trigger an event when rotation is certain amount):
AFRAME.registerComponent('trigger-on-look-behind', {
schema: {type: 'string'},
init: function () {
var eventName = this.data;
this.el.addEventListener('componentchanged', function (evt) {
if (evt.name !== 'rotation') { return; }
if (evt.newData.y < 180) {
this.emit(eventName);
}
});
}
});
And then:
<a-camera trigger-on-look-behind="looked-behind"></a-camera>

Can twitter flightjs do component within component

In Facebook react.js, you can compose component within component, or maybe mix and match.
I'm wondering if twitter flight can do the same thing. if so, can anyone gives me an example?
this is what I have so far:
define(function (require) {
var defineComponent = require('flight/lib/component'),
infoInput = require('component/info_input');
return defineComponent(inputSection, infoInput);
function inputSection () {
this.after('initialize', function() {
infoInput.doSomehting();
});
};
});
and my info_input.js is defined below:
define(function (require) {
var defineComponent = require('flight/lib/component');
return defineComponent(infoInput);
function infoInput() {
this.after('initialize', function() {
});
this.doSomething = function() {
alert('I will do something');
};
};
});
This is what mixins are for.
Flight Components are enriched mixins.
From doc/component_api.md
It comes with a set of basic functionality such as event handling and Component registration. Each Component definition mixes in a set of custom properties which describe its behavior.
Read more about Components.
So the answer to your question is Yes.
I guess that what you are doing is legit, although I've never done it before.
I'd rather move the shared logic to a Mixin or attach the two components to the same element and let them talk via events:
component/input_section.js
this.after('initialize', function () {
this.trigger('uiSomethingRequired');
});
component/info_input.js
this.after('initialize', function () {
this.on('uiSomethingRequired', this.doSomething);
});
Solution mentioned by G.G above works!
We may go a step ahead to trigger events on restricted scope instead of document:
component/input_section.js
this.after('initialize', function () {
this.$node.closest(this.attr.parentClass).trigger('uiSomethingRequired');
});
component/info_input.js
this.after('initialize', function () {
this.on(this.$node.closest(this.attr.parentClass), 'uiSomethingRequired', this.doSomething);
});

jqueryui dialog event: how to attach an even only once

JqueryUI:
The code below fires an alert every time the box is closed, but how can I make it so it only does this for once and not every time.
$("#box").dialog({
close: function () {
alert(999);
}
});
This was how I did it before using jQueryUi:
$("#box").one("click", function () {
alert(999);
return false
});
According to the docs, the .close() method also has a corresponding event: dialogclose. So you should be able to do this:
$("#box").one("dialogclose",function() {
alert(999);
});

Resources