I am making an A-Frame site and I need to create a button to move a ball with the id:"test" to it's starting position: 0 8 0. I've tried with the setAttribute script and it doesn't work. This is the javascript code I'm currently working with:
AFRAME.registerComponent('ballreset', {
events: {
click: function(evt)
{
document.getElementById('test').setAttribute('position', {x:0, y:8, z:0});
}
}
});
Edit: I found a typo in the code. But didn't solve problem
Without a full example its hard to tell what exactly is the problem but,
make sure you do have a raycaster - based cursor component. Mouse clicks won't work with webGL renders like they do with HTML elements.
make sure the click listener is working, ie. by logging each click
make sure the element with the given id exists at the time of the click
Below is a working version of something similar to what you want to achieve (click any object):
<script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent('foo', {
events: {
click: function(evt) {
// grab the current position
let pos = this.el.getAttribute("position");
// move upwards
this.el.setAttribute('position', { x: pos.x, y: pos.y + 0.25, z: pos.z });
}
}
});
</script>
<!-- attach a cursor component -->
<a-scene cursor="rayOrigin: mouse">
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" foo></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" foo></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" foo></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" foo></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
Related
I am creating a vr scene using A-frame (https://aframe.io) and I am wondering how can I animate a gltf model to always follow the camera. For example, I would like the use the animation property of A-frame and position my model so that it always follows the player. If the player moves 10 meters forwards, the gltf will animate 10 space forwards. If the player moves 10 spaces to the left, the camera will follow the player no matter where the player moves. I want to gltf model to always follow the camera. How can this be done? Code for my gltf model:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<a-scene>
<a-gltf-model class="cube" mixin="cube" animation=' property: position; dur: 2500; from: 0 2.3 -1; to: 0 2.5 -1; dir: alternate; autoplay: true; easing: linear; loop: true;' src="https://cdn.glitch.com/bb5471f0-16f5-4309-8c7c-52310dc4ff58%2FRobotfr.glb?v=1625527911166" position="0 2.3 -1"scale="1.2 1.2 1.2" speech-command__show="command: assistant; type: attribute; attribute: visible; value: true;"speech-command__hide="command: hide; type: attribute; attribute: visible; value: false;"></a-gltf-model>
</a-scene>
Actually you are asking something related to third person perspective of camera movement. This can easily done by reparenting your camera, and you need to add some offset to camera so it looks in front of camera always
<a-camera>
<!-- The object with the same transform as the camera + some offset -->
<a-entity gltf-model="./scene.gltf" position="0 0 -0.5"></a-entity>
</a-camera>
if this doesn't work try creating and using a component like this
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-orbit-controls#1.2.0/dist/aframe-orbit-controls.min.js"></script>
<script>
AFRAME.registerComponent("follow-box", {
schema: {
target: {type: "selector"}
},
tick: (function() {
const tmpv = new THREE.Vector3();
return function(t, dt) {
if (!this.data.target) return;
const target = this.data.target.getObject3D("mesh");
const position = target.getWorldPosition(tmpv);
this.el.object3D.position.lerp(tmpv, 0.5)
}
})()
})
</script>
<a-scene>
<!-- camera -->
<a-entity follow-box="target: #player" look-controls>
<a-entity camera position="0 1.6 2"></a-entity>
</a-entity>
<a-entity id="player" gltf-model="./scene.gltf" wasd-controls="speed: 35" ></a-entity>
</a-scene>
I'm trying to make a component that checks the current position of a sphere in an AFrame scene and when it hits a specific coordinate and when it does it fires an event (In example below it resets it to its default position):
AFRAME.registerComponent("trackball", {
update: function() {
let pos = this.el.getAttribute("position");
if (pos == {x:-21.821,y: 1,z: 0})
{
this.el.setAttribute("position", "-21.821 5 0");
}
}
});
I'm not sure what format is returned when .getAttribute("position") is called so that may be why it's not working. I am running AFrame 1.1.0.
First of all update is called when attributes are changed via setAttribute(). If you want a function that is called on each render frame, then use tick().
Secondly, try using a range, instead of a fixed point, it's very likely that the object will move past the point between two ticks.
Something like this:
<script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/n5ro/aframe-physics-system#v4.0.1/dist/aframe-physics-system.min.js"></script>
<script>
AFRAME.registerComponent("trackball", {
tick: function() {
let pos = this.el.getAttribute("position");
if (pos.y < 0.5) {
// reset position
this.el.setAttribute("position", "0 3 -4")
// sync
this.el.components["dynamic-body"].syncToPhysics();
}
}
});
</script>
<a-scene physics cursor="rayOrigin: mouse">
<a-sphere position="0 1.25 -5" radius="0.25" color="#EF2D5E" dynamic-body trackball></a-sphere>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
Also try using the object3D properties instead setAttribute() and getAttribute() when dealing with frequently called functions (which certainly applies to tick()):
<script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/n5ro/aframe-physics-system#v4.0.1/dist/aframe-physics-system.min.js"></script>
<script>
AFRAME.registerComponent("trackball", {
// iife to initialize the "origin point" once
tick: (function() {
const origin = new THREE.Vector3(0, 3, -4);
const y_range = 0.5;
return function() {
// check if the ball is out of range
const pos = this.el.object3D.position
if (pos.y < y_range) {
// reset position
pos.copy(origin);
// sync
this.el.components["dynamic-body"].syncToPhysics();
}
}
})()
});
</script>
<a-scene physics cursor="rayOrigin: mouse">
<a-sphere position="0 1.25 -4" radius="0.25" color="#EF2D5E" dynamic-body trackball></a-sphere>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
Keep in mind, updating the position in such manner is more performant, but will cause getAttribute("position") to return the last position set via setAttribute("position", new_position)
I am trying to build a simple scene with A-Frame in JS to excercise the basics(codepen). So far so good I got the geometry like I want it, not perfect but it's ok.
<a-cylinder position="-4.8 1 0" radius="0.2" height="2" color="slategray">
<a-cylinder position="0 1.5 0" radius="0.15" height="1.5" color="slategray">
<a-box position="-1 0.5 0" width="2" height="0.5" depth="0.3" color="slategray">
<a-circle cursor-listener id="green" position="-0.5 -0.03 0.17" color="#196f3d " radius="0.2"></a-circle></a-circle>
<a-circle cursor-listener id="yellow" position="0 -0.03 0.17" color="#7d6608 " radius="0.2"></a-circle>
<a-circle cursor-listener id="red" position="+0.5 -0.03 0.17" color="#641e16" radius="0.2"></a-circle>
</a-box>
</a-cylinder>
</a-cylinder>
<a-ring position="0 0 0" rotation="-90 0 0 " color="gray" radius-inner="5" radius-outer="7">
<a-sphere id="car" position="-0 -1 0.4" radius="0.01" animation="property:rotation;to:0 0 -360; easing:linear; dur:5000;loop:false;autoplay:false">
<a-box position="-5.5 0 0" width="0.4" height="0.4" depth="0.4" ></a-box>
</a-sphere>
</a-ring>
I can make the rectangle orbit around the center of the scene, but what i want to do is make the animation a click event based, so I though of switching the loop attribute from false to true.
i went ahead and tried it in the console but nothing changed.
var s=document.getElementById("car")
s.components.animation.animation.loop=true
Note: I've also changed the autoplay to false and tried switching it back from the console by executing
var s=document.getElementById("car")
s.components.animation.animation.autoplay=true
but again nothing changed.
I think I misunderstood some in the documentation, I'm not sure exactly.
Can somebody please help me.
---------------------------------EDIT------------------------
I've added these events setting to the animation attribute of the the parent of component of my box
startEvents: rotation-start; pauseEvents: rotation-pause; resumeEvents: rotation-resume;
e.g :
<a-sphere id="car" position="-0 -1 0.4" radius="0.01" animation="property:rotation;startEvents: rotation-start; pauseEvents: rotation-pause; resumeEvents: rotation-resume;to:0 0 -360; easing:linear; dur:5000;loop:true;">
<a-box position="-5.5 0 0" width="0.4" height="0.4" depth="0.4" ></a-box>
</a-sphere>
so now I can play/pause the animation in the browser console using :
var e=document.getElementById("car")
e.components.animation.animation.play()
e.components.animation.animation.pause()
but trying to do it inside script tag so that i can utilize the cursor-listener event doesn't work :
<script>
AFRAME.registerComponent('cursor-listener', {
update: function () {
this.el.addEventListener('click', function (evt) {
var car=document.getElementById("car");
if(this.id=="red"){
car.components.animation.animation.pause()
}
if(this.id=="green"){
console.log(this.data);
this.components.animation.animation.play()
}
});
}
});
</script>
throws error:
TypeError: this.components.animation is undefinedtraffic_light.html:23:21
---------------------------------------------------EDIT N°2-------------------------
I fixed it , here is the new js :
<script>
AFRAME.registerComponent('cursor-listener', {
update: function () {
this.el.addEventListener('click', function (evt) {
var car=document.getElementById("car_origin");
if(this.id=="red"){
car.components.animation.animation.pause()
}
if(this.id=="green"){
car.components.animation.animation.play()
}
});
}
});
</script>
I was using this exact code in the previous version of Aframe 0.8.0 And the clicking was working normally.
However, when I tried using the 0.8.2 or the master version it showed no signs of clicking and without showing any errors
This is my registered component which I would like to detect clicks on the entities it is attached to
AFRAME.registerComponent('change-color-on-hover', {
init: function () {
var data = this.data;
var el = this.el; // <a-box>
var defaultColor = el.getAttribute('material').color;
/**
* Attach 'click' event
*/
el.addEventListener('click', function () {
alert('clicked');
});
}});
The Element where I want to detect the click (I am setting its position later and they it's appearing where they should be)
<a-image position="" src="#blue_target_rendered" height="30" width="40" depth="1" shadow event-set__click="_event: click; color:black" change-color-on-hover look-at="[camera]" id="hotspot-{{$hotspot->id}}" data-link-to="{{$hotspot->link_to}}" data-link-from="{{$hotspot->link_from}}">
With a setup like this:
<a-entity id="cameraParent" position="0 0 0" >
<a-entity id="cam" camera="zoom:1;" look-controls collider-check position="0 0 0" >
<a-entity cursor=" rayOrigin: mouse" geometry="primitive: ring; radiusInner: 0; radiusOuter: 0" material="color: black; shader: flat"></a-entity>
<a-entity raycaster="showLine: true; far: 1000"></a-entity>
</a-entity>
</a-entity>
The line component created by the raycaster is interfering with the rays emitted by the cursor = rayOrigin: mouse. In other words, you're clicking on the element with the line on each click.
If you reposition the raycaster a bit:
<a-entity position="0 -0.1 0" raycaster="showLine:true; far: 1000"></a-entity>
The mouse cursor should be working fine.
i am developing an Aframe WebVR app, trying to write a function that will split a this.id into two parts and giving it to a variable then .setattribute
this is my code
AFRAME.registerComponent('remove-yellow', {
init: function () {
this.el.addEventListener('click', function (evt) {
console.log(this.id);
var boxid = this.id.split("-")[0];
console.log(boxid);
boxid.setAttribute("animation__scale", "property: scale; from: 1 1 0.01; to: 0.001 0.001 0.001; dur: 150");
});
}
});
with var boxid, console will give uncaught TypeError: boxid.setAttribute is not a function.This is the box that I am trying to animate:
<a-box id="box1" position="0 2 -1.5" rotation="0 0 0" depth="0" width="1" height="1" color="#39dbd8" scale="0.001 0.001 0">
<a-entity id="info" width="1" position="0 0 0.6" text="value: Hello people what is going on AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; color:#000;"></a-entity>
<a-box id="box1-close" class="clickable" remove-yellow id="box2" position="0.4 0.4 0.6" rotation="0 0 0" depth="0" width="0.15" height="0.15" color="#f00" scale="1 1 0">
</a-box>
</a-box>
<a-box id="box1-show" class="clickable" add-yellow id="box3" position="0 2 -2" rotation="0 0 0" depth="0" width="0.5" height="0.5" color="#008000" scale="1 1 0"></a-box>
when box1-show is clicked, its id will be split"-", box1 will then receive the animation attribute. If I write:
box1.setAttribute("animation__scale", "property: scale; from: 1 1 0.01; to: 0.001 0.001 0.001; dur: 150");
it will work fine.But with var boxid, console will give uncaught TypeError: boxid.setAttribute is not a function.
I have tried the codes below from other solutions I found:
$(boxid).attr('animation__scale', 'property: scale; from: 1 1 0.1; to: 0.001 0.001 0.001; dur: 150');
the error will disappear but it will not animate.
I am thinking it might be a syntax error, anyone have any ideas?
At first the id is box1-close, then its redeclared as box2. Its best to use the id only for identification purposes, not to smuggle data :)
Normally You could use the global data attribute:
<div data-id="box1"></div>
But considering it's best to fully utilize the a-frame component system,
You need to use the component's schema.
Should the component manipulate any other element on the scene, just do
<a-entity my-component="param: value">
and access it in the component by the reference this.data.param.
Moreover the id, split or not, is just a string, you can't set any attributes to it, hence the error. If you want to set an attribute to the element it represents, you can grab it using:
document.querySelector("#" + value).setAttribute(), or more properly with document.getElementById(value).setAttribute().
You can check it out in my fiddle.