Using A-Frame creates a lot of clutter. Is their a way to access the code from another file? If not then what do you recommend for preventing cluster? My code is public # https://repl.it/#ColbzDaBoss1/A-Frame-Test
Thanks :)
Colby Bianco
Use components: https://aframe.io/docs/0.9.0/introduction/writing-a-component.html
<script src="my-component.js"></script>
<a-scene>
<a-entity my-component></a-entity>
</a-scene>
component file:
AFRAME.registerComponent('my-component', {
init: function () {
}
});
Related
I am writing a custom component that I would like to define other component dependencies.
The dependencies are different animations types.
Let's say they have the names "animation__x" and "animation__y"
x and y can be any name, so I am looking for something like animation__*
or /animation__\s*/
The only way I have made this work at the moment is either ensuring my component is placed after the animation components on the HTML or alternatively to force update components using this.el.updateComponents()
Neither of these solutions feels right to me.
AFRAME.registerComponent('cool-component', {
dependencies: ['animation'],
update: functions(data){
//detect available animations and do some stuff with them
let animations = Object.keys(components).filter((key) => {
return /(^animation__\w*)/.test(key);
});
//animations results in an empty array
}
});
html that is not working
<a-scene cool-component animation__x="" animation__y="" animation__z=""></a-scene>
html that is working (but its not good as I cant ensure my component is always last in the list
<a-scene animation__x="" animation__y="" animation__z="" cool-component></a-scene>
js that works, but doesnt feel write as I am using the entities internal functions
AFRAME.registerComponent('cool-component', {
dependencies: ['animation'],
update: functions(data){
this.el.updateComponents(); //<-- I DONT LIKE THIS BUT IT WORKS
//detect available animations and do some stuff with them
//now all animations are available as this.el.components
let animations = Object.keys(components).filter((key) => {
return /(^animation__\w*)/.test(key);
});
}
});
Three options:
Depend on the specific component names: dependencies: ['animation__xxx']
Make cool-component set those animations:
AFRAME.registerComponent('cool-component', {
init: functions(data){
this.el.setAttribute('animation__xxx', {...});
}
});
You can also defer cool-component logic until the entity has loaded and all the components have initialized:
init: function () {
this.el.addEvenListener(‘loaded’, this.doStuffAferComponentsLoad.bind(this));
}
More details in what cool-component is trying to accomplish will help to get a more precise answer.
A-Frame Mixins go into the <a-assets> element, which must be defined before the scene is rendered. This makes sense for pre-loading/caching images, videos etc, but it seems there should be a way to dynamically create and use mixins.
Just adding the mixin to <a-assets> at runtime does not seem to work. The recommendation for adding image assets at runtime is to inline the image source and set it on the material directly. Is there some similar way of defining/altering a mixin at runtime? Or do I just need to set the relevant properties on all the objects to which the mixin is applied (taking care to also account for properties having been set by other mixins later in the mixin chain or directly on the object itself)
Edit: It looks like aframe-asset-on-demand-component is designed to do this for image/video assets. Unclear if works for mixins, but it also hasn't been updated in a year. Is this a (semi-)officially recommended solution?
Sorry if I've misunderstood your question but I seem to be able to add mixins to the assets tag at runtime. A basic version would mean writing a component as follows;
// add assets at run time
AFRAME.registerComponent('addasset', {
init: function () {
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
},
});
And then attach that component to the scene as follows;
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
mixin="makeitred"
position="0 0.5 -3">
</a-cylinder>
</a-scene>
Here is a working glitch
To demonstrate how that could be added once the scene is running here is a version of the same component with a setTimeout to demonstrate how the mixin could be added later on.
// add assets at run time, delayed
AFRAME.registerComponent('addasset', {
init: function () {
setTimeout(function(){
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
var cylinder = document.getElementsByTagName('a-cylinder')[0]
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
cylinder.setAttribute('mixin', 'makeitred')
}, 2000);
},
});
and then the HTML in which the mixin attribute will be added later
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
position="0 0.5 -3">
</a-cylinder>
</a-scene>
Here is a glitch of that
And for the sake of exploration, here is the same set up but triggered by an example event. First the same component but with an event listener
// add assets at run time, triggered by event
AFRAME.registerComponent('addasset', {
init: function () {
document.addEventListener("testevent", function(){
var assets = document.getElementsByTagName('a-assets')[0]
var mixin = document.createElement('a-mixin')
var cylinder = document.getElementsByTagName('a-cylinder')[0]
mixin.setAttribute('id', 'makeitred')
mixin.setAttribute('material', 'color: red')
assets.appendChild(mixin)
cylinder.setAttribute('mixin', 'makeitred')
});
},
});
Then a component that emits an event for testing
// test event to trigger adding of mixin
AFRAME.registerComponent('testevent', {
init: function () {
var self = this.el
setTimeout(function(){
self.emit("testevent")
}, 3000);
},
});
Then the HTML, as before but including a test entity that emits an event
<a-scene addasset>
<a-assets>
</a-assets>
<a-cylinder
position="0 0.5 -3">
</a-cylinder>
<a-entity
testevent>
</a-entity>
</a-scene>
And here is a glitch for that
So you could mix those up, add the mixin to assets but delay/trigger on event the addition of properties or add the mixin to assets with properties but delay/trigger on event the setting of that attribute on your target elements.
I hope that helps
I have the following assets:
<a-assets>
<a-asset-item id="model-obj" src="the-source..."></a-asset-item>
<a-asset-item id="model-mtl" src="another-source..."></a-asset-item>
</a-assets>
And after the scene is loaded I attached the following event listener which is never called and I don't know why (although the model loads and is shown in the scene):
document.querySelector('#model-obj').addEventListener('loaded', function() {
console.log('loaded');
});
According to the docs it should work (https://aframe.io/docs/0.5.0/core/asset-management-system.html#lt-a-asset-item-gt).
That's odd. This code works for me (but I'm using a glTF model):
<a-assets>
<a-asset-item id="duck" src="duck/duck.gltf"></a-asset-item>
</a-assets>
and
document.getElementById('duck').addEventListener('loaded', function() {
alert('ok')
})
A few pointers:
Is your script running after you've defined the element?
Does the network inspector show you an unsuccessful load (HTTP 404, 500, etc.)?
Any JS errors on your page in the console?
I see that A-Frame is used to build virtual reality experiences with just markup/HTML. How can I use JavaScript alongside the A-Frame markup elements?
A-Frame is a JavaScript/three.js framework, and the HTML is just the outermost declarative layer.
Manipulating the DOM
https://aframe.io/docs/0.2.0/core/entity.html
All elements/objects in A-Frame are entities. We can manipulate these elements using standard DOM methods.
var boxEl = document.querySelector('a-box');
boxEl.setAttribute('width', 5);
boxEl.setAttribute('color', '#FFF');
boxEl.addEventListener('click', function () {
// If we are using the cursor component.
boxEl.setAttribute('color', '#FFF');
});
boxEl.emit('some-event');
boxEl.removeAttribute('color');
boxEl.querySelectorAll('a-sphere');
var sphereEl = document.createElement('a-sphere');
sphereEl.setAttribute('radius', 1);
document.querySelector('a-scene').appendChild(sphereEl);
sphereEl.addEventListener('loaded', function () {
console.log('sphere attached');
});
Entity-Component
https://aframe.io/docs/0.2.0/core/
A-Frame is built on an entity-component-system pattern (ECS), popular in game development and used in engines such as React and PlayCanvas. In ECS, every object (or entity) is created from the ground up by composing components (not to be confused with Web Components). Components add logic, behavior, appearance to entities.
https://aframe.io/docs/0.2.0/core/component.html
We can encapsulate JS within components:
AFRAME.registerComponent('color-on-click', function () {
schema: {
color: {default: 'blue'}
},
init: function () {
var el = this.el; // Reference to the entity.
var data = this.data; // Data passed in through HTML, defined in schema.
el.addEventListener('click', function () {
el.setAttribute('color', data.color);
});
}
});
And attach these components to our entities in HTML. Notice how we are declaratively attaching/plugging in JS to objects in HTML that can take inputs!:
<a-box color="red" color-on-click="color: blue"></a-box>
<a-sphere color="orange" color-on-click="color: white"></a-sphere>
And these components can do anything beyond simple JS. We have access to the entire three.js API so anything in three.js can be easily wrapped. In fact, we wrap any JS library in the world we want in components and use them declaratively.
Manipulating Components
Manipulating properties of components is similar to manipulating HTML attributes. <a-box> consists of geometry and material components. So it looks like under the hood:
<a-entity id="box" geometry="primitive: box" material="color: red" scale="2 2 2"></a-entity>
We can manipulate the individual properties:
var boxEl = document.querySelector('#box');
boxEl.setAttribute('geometry', 'width', 5);
boxEl.getAttribute('material').color;
boxEl.removeAttribute('scale');
Integration
By exposing its API in HTML, A-Frame works well with existing web frameworks and libraries like d3, React, Angular, templating engines.
aframe-react
d3 example
d3 in a-frame component
I am trying to create a very simple HTML page with Polymer, which includes an HTML file with all the components I need:
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="components/my-element.html">
my-element.html looks like this:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="my-element">
<template>
// Some HTML
</template>
<script>
Polymer({
is: 'my-element',
properties:{
// properties
}
});
</script>
</polymer-element>
When I run it as it is, everything works fine.
When I try to Vulcanize the components.html file, and then open the same page with the Vulcanized version, I get the following error in the console
"Uncaught TypeError: prototype.registerCallback is not a function"
I have noticed the Vulcanize process turns
Polymer({
is: 'my-element',
properties:{
// properties
}
});
Into
Polymer('my-element', {
is: 'my-element',
properties:{
// properties
}
});
Which seems to be what is causing the bug, as window.Polymer only expects an Object as a parameter.
I am using grunt-vulcanize to do the actual Vulcanizing, and my config looks like this:
vulcanize: {
default: {
options: {
excludes: {
imports: [
'polymer.html'
]
},
'strip-excludes': false,
inline: true,
strip: true
},
files: {
'build.html': 'components.html'
},
},
}
Is there a way of stopping this?
I am using Polymer 1.0, and grunt-vulcanize 0.6.4
Got it: I was using <polymer-element> instead of <dom-module>. <dom-module> is correct for Polymer 1.0.
This guide is useful, and has taken me days to find, if only so that you know what works with 0.5 and what works with 1.0:
https://www.polymer-project.org/1.0/docs/migration.html
grunt-vulcanize is not compatible with vulcanize 1.8.1, because last update on github was on Feb 9 with version 0.6.4. It's out of date plugin for Polymer 0.8+.
temporary adaptation for grunt-vulcanize which works with vulcanize ^1.8.1 and Polymer 1.0.
https://github.com/kgadzinowski/grunt-vulcanize/
just change in package.json:
"grunt-vulcanize": "kgadzinowski/grunt-vulcanize"
it'll work fine