I am looking to add a piece of text to the camera on an A-frame scene which will act as a link to transfer to a non-VR page.
<a-entity camera look-controls position="0 0 0">
<a-cursor visible="false">
</a-cursor>
<a-entity text="value: Galleries; color: black; width: 2;" position="-0.25 0.7 -1" onClickLink>
</a-entity>
</a-entity>
The onClickLink function is registered and uses window.location.href = "https://www.google.com"; to change page as suggested in the docs.
I have created a gallery, and want to give users a simple UI link at the top to take them back to a list of other galleries - however - in the example above the onClickLink function just gets applied to the whole scene rather than only the piece of text. I guess it has maybe been applied to its parent - the camera - so any click counts as firing the event? Or do I need to add something to the event to determine what object was clicked? I'm not sure how raycasting would work on something in the top left corner of the camera?
I do not want to use a 3D object in the scene, and I similarly don't want to use the Link portal entity that will be placed in the scene (I did try going down this route similar to the above, and it again just applied the link to the entire scene rather than on clicking the link itself).
Is there a way to achieve what I'm after?
You can use standard html elements and a-frame element to gather and use Css (position: fixed; z-index: 999) to bring it forward to the top but to create an external html link I share part of my code hope you find the idea, you can use jquery(or pure javascript) to add click event to your a-frame objest, but first add below code to <a-camera tag:
raycaster="far: 10000; objects: .clickable"
then add "clickable" class name to any A-frame object you want to be clicked by user
<a-image visible=true id="web" class="clickable external" src="#imgweb" alpha-test="0.5" position="-0.14 -0.9 0" height="0.18" width="0.18"></a-image>
and then like any html dom object you can access to aframe objects by javascript:
window.onload = function() {
$(".external").each(function(index) {
$(this).on("click", function() {
console.log(this.id + ' Pushed');
switch (this.id.toLowerCase()) {
case 'web':
window.open("http://www.farsix.com/");
break;
case 'instagram':
window.open("https://www.instagram.com/");
break;
default:
console.log("Unknown Link!");
};
});
});
};
Related
For my A-Frame project I want the player to move forward without depending on any controllers or keyboards. The player should always be moving forward, and should be able to decide the direction by looking around (without changing the z value). Using look-controls on the camera and movement-controls (from aframe-extras) on the rig gives this behavior, apart from the fact that it still depends on a controller. How can I implement auto walk in A-Frame?
<a-entity id="rig" movement-controls>
<a-entity id="camera" position="0 1.7 0" camera look-controls>
</a-entity>
If you are using the movement-control component, you can add your own "controls". As the doc say, all you have to do is to name it with a "-controls" suffix (see https://github.com/n5ro/aframe-extras/tree/master/src/controls#customizing-movement-controls ). This is the code for a simple "autowalk" custom controls component for "movement-controls":
AFRAME.registerComponent('autowalk-controls', {
isVelocityActive: function () {
return true;
},
getVelocityDelta: function () {
return new THREE.Vector3(0, 0, -1);
}
});
And you can simply use it by adding it to the controls proprety of "movement-controls" like that:
movement-controls="controls: autowalk"
I am using the updated super-hands component in a-frame. I have replicated the basic example from the docs (see below).
<head>
<title>Most Basic Super-Hands Example</title>
<script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v4.1.2/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/super-hands#3.0.0/dist/super-hands.min.js"></script>
</head>
<body>
<a-scene>
<a-assets></a-assets>
<a-entity>
<a-camera></a-camera>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="left"></a-entity>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="right"></a-entity>
</a-entity>
<!-- hover & drag-drop won't have any obvious effect without some additional event handlers or components. See the examples page for more -->
<a-box hoverable grabbable stretchable draggable dropppable color="blue" position="0 0 -1"></a-box>
</a-scene>
</body>
Which works well out of the box when it comes to grabbing objects and being able to affect their position, but not their rotation. I wondered if it was possible, (without using physics and adding a static body to the controller) to affect the rotation of the targeted objects?
I have been able to get the rotation working by adding the relevant mixin and phase-shift component from this example but given it seems possible to grab objects affecting their position I wondered if I could modify the basic example to change the rotation instead of the position (ideally I would be able to choose whether to affect position, rotation or both). I guess I'm imagining perhaps creating another component like rotatable or maybe a build on the grabbable component like grabbable="property: rotation" that I could add to the target objects.
I would like to know if this is possible in general as I would like to understand the component better so as to have more control. To give some context for this particular question however, I have a situation where the super-hands controllers are children of a dynamic-body and once a static-body is added, it causes problems with the behaviour of the parent dynamic-body.
Any advice appreciated as ever, if you need any more info, please let me know.
If you want the rotation without physics, you'll need to implement your own version of grabbable that does the matrix math. It would go something like this:
tick: (function () {
const grabeeMatrix = new window.THREE.Matrix4()
const ignoreScale = new window.THREE.Vector3()
return function () {
if (this.grabber) {
grabeeMatrix.multiplyMatrices(
this.grabber.object3D.matrixWorld,
this.grabOffsetMatrix
)
grabeeMatrix.multiplyMatrices(this.parentOffsetMatrix, grabeeMatrix)
// using decomp over direct Object3D.matrix manipulation
// keeps in sync with other A-Frame components
grabeeMatrix.decompose(
this.el.object3D.position,
this.el.object3D.quaternion,
// let stretchable manage scale
ignoreScale
)
}
}
})(),
resetGrabber: function () {
if (!this.grabber) {
return false
}
this.grabber.object3D.updateMatrixWorld()
this.el.object3D.parent.updateMatrixWorld()
// save difference between grabber world matrix and grabee world matrix
this.grabOffsetMatrix
.getInverse(this.grabber.object3D.matrixWorld)
.multiply(this.el.object3D.matrixWorld)
// save difference between grabee world and local matrices
this.parentOffsetMatrix.getInverse(this.el.object3D.parent.matrixWorld)
return true
},
I have an entity and I want when I click or point with the a-frame cursor that is changes the attribute visible of another entity to true.
<a-entity id="pug" gltf-model="#pug" position="-1.75 0.0035 3"
scale="0.01 0.01 0.01" rotation="0 -11 0" >shadow
event-set__down="_event: mousedown; scale: 1.2 1.2 1.2"
event-set__up="_event: mouseup; scale: 1 1 1"
event-set__leave="_event: mouseleave; scale: 1 1 1">
</a-entity>
If I have that the interaction with the cursor works but I don't how (or even if) I can make it affect the attribute of another entity.
I want that :
<script>
$(document).ready(function(){
$("#pug").mouseenter(function(){
$('#bubble').attr('visible', 'true');
});
});
</script>
I do have cursor in my camera but I don't know... I'm new to A-Frame and I must be missing something :/ Thanks!
Keep in mind, that changing CSS properties won't affect the rendered entities. The best way to change properties is by using entity.setAttribute("attribute", "value")
I'm not sure if you can do a switch (visible / invisible) using the event-set component, but you can make an entity visible, or invisible by setting the visible attribute:
<a-entity event-set__click="_target: a-cylinder; visible: false;"></a-entity>
check it out here.
But i would recommend creating an a-frame component. You can check them out in the docs, in this case it looks like this:
AFRAME.registerComponent("foo", {
init: function() {
this.el.addEventListener("click", (e) => {
let cylinder = document.querySelector("a-cylinder")
cylinder.setAttribute("visible", !cylinder.getAttribute("visible"))
})
}
})
HTML:
<a-entity foo>
quite simple:
1) The AFRAME.registerComponent("foo" bit "declares" the component.
2) The init: function() function is executed when the component is initialized
3) Inside the click listener, I've made a simple toggle, which sets the visibility to the opposite of the actual value. If its visible, switch it to !visible = false.
Check it out here.
I have a text element and a skybox in my scene. When the scene initialises, I want the text to animate its position once.
<!-- scene elements -->
<a-scene coogee2006-scene embedded style="width:100%;height:400px;">
<a-assets>
<img
id="coogee2006"
src="/assets/vr/sydney-coogee-3-peter-gawthrop.jpg"
preload="auto">
<audio
id="beachsound"
src="/assets/vr/beach1.wav"
preload="auto">
</a-assets>
<a-sky src="#coogee2006"
rotation="0 -90 0">
</a-sky>
<!-- text animates in on startup (see searise_vr.js) -->
<a-text
id="coogee2006-text"
value="Coogee, Sydney\n2006"
position="5 12.5 -50"
rotation="0 -15 0"
scale="20 20 20"
visible="true"
text="anchor:align;alphaTest:0.2;width:5;
value:COOGEE, SYDNEY\n2006;
zOffset:0;color:#000;font:exo2bold"
sound="src:#beachsound;autoplay:true;loop:true;volume:20;">
<a-animation
attribute="position"
dur="3000"
begin="coogeetour"
to="12.5 12.5 -50"
easing="ease-in"
fill="both"
repeat="0">
</a-animation>
</a-text>
</a-scene>
If I set a static delay with begin=5000, it works fine, but if I try to set it on an event, like begin="coogeetour", the animation doesn't occur. I've tried firing the event two ways:
First, by registering a component for the scene, in a script tag above the a-scene tag, and using document.querySelector() identify the text element:
<script>
AFRAME.registerComponent('coogee2006-scene', {
// emit text events when the scene is initialised
init: function () {
console.log('Running coogee2006-scene.init()');
document.querySelector("#coogee2006-text").emit('coogeetour');
}
});
</script>
Second, by registering a component for the text element and using this.el, as in the A-Frame Writing a Component section, and putting this in an external file that is linked:
AFRAME.registerComponent('coogee2006-text', {
// emit text events when the scene is initialised
init: function () {
console.log('Initialising text element');
this.el.emit('coogeetour');
}
});
In either case, the console.log works, so the component is initialising, but the animation isn't happening. I can't find coogeetour in the elements' event listeners when debugging, but I don't know if that's because emit() isn't working properly or because it oughtn't show up in the debugging.
EDIT: here's my console log on loading:
Navigated to http://127.0.0.1:4000/private/JoQyfM/
index.js:73A-Frame Version: 0.5.0 (Date 10-02-2017, Commit #110055d)
index.js:74three Version: ^0.83.0
index.js:75WebVR Polyfill Version: dmarcos/webvr-polyfill#a02a8089b
browser.js:117 core:a-assets:warn Asset loading timed out in +0ms 3000 ms
three.js:19590 THREE.WebGLRenderer 83
(index):81 Running coogee2006-scene.init()
browser.js:117 components:sound:warn All the sounds are playing. If you need to play more sounds simultaneously consider increasing the size of pool with the `poolSize` attribute. +118ms
three.js:17507 THREE.WebGLRenderer: image is not power of two (5980x2990). Resized to 8192x4096 <img crossorigin="anonymous" src="/assets/vr/sydney-coogee-3-peter-gawthrop.jpg">
I am not sure exactly why, but it looks like the component methods are swallowing the emit() event. I tried putting that same call in the update() function but it still did not work.
What did work was to put the emit call in a timeout:
setTimeout(function() { document.querySelector("#coogee2006-text").emit('coogeetour'); }, 0);
Codepen: http://codepen.io/shanabus/pen/zZYBaa
Okay, it looks like begin only works with numerical values; delay works with both numerical values and event names. To be fair, this is described in the attribute table for animations, but the block below it on the begin attribute shows an example of it being used with an event name. Maybe a depreciated attribute?
EDIT: okay, maybe this isn't the answer. I'm not entirely sure why delay and begin both exist—is it so there can be a delay following an event trigger, or is delay just depreciated?
What is the best way to hide an element using A-Frame?
Do I need to remove the element from the DOM?
var el = document.querySelector("#yourElementId");
el.setAttribute("visible",false);
The easiest way to hide an element is the visible attribute:
myElement.setAttribute("visible", false)
You can also specify it on the a-frame tag itself e.g.:
<a-image id="hand-overview-chart"
src="#handOverviewImg" position="3 3 0"
width="4" height="4" visible="false">
</a-image>
Of course you'll still need javascript to trap on some event like "mouseenter" to toggle it visible:
document.querySelector('#myElParentId').addEventListener('mouseenter',myEventHandler);
myEventHandler: function (evt) {
let myEl = document.querySelector("#hand-overview-chart");
myEl.setAttribute("visible","true");
}
The problem with visible="false" is that the element behind it will not be clickable (because the element is there, but is transparent). So, the alternative I found is to make it with zero width.
Here is how I hide the element on mouse click:
document.getElementById("my_element_id").addEventListener('mousedown', function(evt) {
document.getElementById("my_element_id").setAttribute("width", "0");
});