How can I hide an element with A-Frame? - aframe

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");
});

Related

How to create a UI link in A-frame

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!");
};
});
});
};

A-frame Want click on entity shows other entity

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.

Fixed text position in A-Frame

I am trying to add fixed text in a-frame. I can add a a-text tag like this:
<a-camera wasd-controls-enabled="true" position="0 1 0" look-controls>
<a-text position="0 0 -1" value="Test" color="#fff"></a-text>
</a-camera>
But this method does not make the text fixed to the side of the screen so the text will overflow when I change the screen size. Is this even possible?
It's generally best practice to add the text to the scene rather than to the camera. This gives the user the option to look in either direction so they can see everything.
https://aframe.io/docs/0.8.0/components/camera.html#fixing-entities-to-the-camera
If you still decide to anchor the text to the camera, the simple solution would be to make sure that the text is at a distance great enough that all of it can be seen comfortably at the smallest screen size (vertical phone orientation, etc.).
A more complex solution would be to change the size of the <a-text> primitive (or containing entity using the text component) on the window.resize event, and adjusting the text container accordingly. This can be done by creating a custom component.
Here is a rudimentary example:
AFRAME.registerComponent('resize-text', {
init: function() {
var self = this;
window.addEventListener('resize', function(e) {
var height = window.innerHeight;
var width = window.innerWidth;
// console.log('resized!', height, width);
self.el.setAttribute('width', ( width / 100 ));
});
}
});
And here is a demo of that code in action: https://codepen.io/dansinni/pen/pVJmQg
Resize the window or drag the center divider and you will see that the text size changes.
Hope this helps.

How can I make my entity spin when the user hovers over it?

I am using the event-set-component to cause my obj model under to increase scale when the cursor hovers over it.
This is working correctly.
But how would I make it spin as well as increase size?
I found the following code on AFrame docs but I do not know how to implement it so it triggers when the mouse is over the entity.
<a-animation attribute="material.opacity" begin="fade" to="0"></a-animation>
As you have asked for a different method in your comment I suggest to use a multi use component like the one I have written:
AFRAME.registerComponent('event-animate', {
schema: {
target: {type: 'selector'},
aevent: {default: 'animation1'},
triggeraction: {default: 'click'}
},
init: function () {
var data = this.data;
this.el.addEventListener(data.triggeraction, function () {
data.target.emit(data.aevent);
});
}
});
So in HTML it would look something like this:
<a-entity id="object1"
event-animate="target:object1;
triggeraction:mouseenter;
aevent:eventstart">
<a-animation attribute="scale"
dur="5000"
begin="eventstart"
from="1"
to ="5"
direction="alternate">
</a-animation>
<a-animation attribute="rotation"
dur="5000"
begin="eventstart"
from="0 0 0"
to="0 360 0"
direction="alternate">
</a-animation>
</a-entity>
The direction="alternate" should bring it back to its original position.
The quoted animation will work, if You set the begin event properly:
<a-animation attribute="rotation"
dur="2000"
begin="mouseenter"
to="0 360 0"
repeat="1"><a-animation>
On mouseenter, the animation triggers, and rotates the entity once.
To gain more control over what You do, You would need to get deep into making components.
1. The Easiest way i can think of, is using both the animation component, and Your own. You would need to set up a component listening for the mouseenter/mousexit, and trigger the animation:
AFRAME.registerComponent('mouseenterhandler', {
init: function () {
let el = this.el; //Your element reference
el.addEventListener('mouseenter, function () {
// get the rotation, by getAttribute, or by accessing the
//object3D.rotation
let rotation = el.getAttribute('rotation');
//lets rotate it to the same position
rotation.y += 360;
//set the animation component accordingly:
el.children[0].setAttribute('to',rotation);
//emit the 'begin' event:
el.emit('startrotating');
});
}
});
Quick Improvement if necessary: disable the listener, when the animation is triggered. Made with a boolean switched on the mouseenter event, and the animationend event.
2. You can choose not to use the animation component, and check on tick() if the cursor is over. If so, rotate the element by the actualRotation.y+0.1 ( or any other desired rotation ).
As noted before, You can access the rotation by getAttribute() or el.object3D.rotation.
As for the scale, You if You need to rotate + rescale the object on the mouseenter event, just add another animation, and use it like i did with the rotation.I'm not sure how it's usually done, in my experience animations are good, when there are not that many interactions, because they sometimes do unexpected things, which You have to predict/find out, and prevent.
On the other hand, making any animation manually ( changing properties on tick ) may seem laggy if the rotation delta is too big. You need to play with it, and find out which suits You best.

In Flex 4.5, how to add a toolTip to an InlineGraphicElement inside a TextFlow (TLF) using ActionScript (not MXML)?

var newParagraph : ParagraphElement = new ParagraphElement();
var icon : InlineGraphicElement = MY_ICON;
// icon.toolTip = "boo" ????
newParagraph.addChild( icon );
I want a specific toolTip for "icon". I have read about using HTML and rollOver and rollOut (e.g. Thanks, Mister), but I'm building this as part of a larger text block; it's difficult to switch from incremental objects to HTML in the middle.
If I cannot set an event on the icon, can I create an HTML bit in ActionScript as part (but not all) of a paragraph ?
Cheers
I just came across the same problem.
The easiest solution i found was wrapping my InlineGraphicElement in a LinkElement.
The LinkElement already dispatches mouse events.
The problem (as I'm sure you found out) is that text flow elements are not display objects, and so do not implement the normal display object behavior.
What you need to do is create a custom InlineGraphicElement that attaches the event listeners you need, then dispatch an event off of the textFlow instance so it can be read in somewhere in your display object hierarchy (or whatever your method of choice is to target that event).
You can see a good example of how to add mouse interaction by looking at the source to the LinkElement (see the createContentElement function).
Unfortunately the InlineGraphicElement is marked final, so you will need to duplicate it's functionality rather then extend it. Just make sure to use the custom graphic element in your code in lieu of the normal one.
Best of luck!
edit
Just in case the point was lost, the idea is that you can capture the mouse event somewhere in your app by attaching a listener to the textFlow, then programmatically create and position a tool tip over the element using the standard methods to find the bounds of a text flow element.
You can do some hit-testing when the mouse is over the textflow component.
Suppose you have some textflow like this:
<s:RichEditableText width="100%" height="100%" mouseOver="toggleTooltip()">
<s:textFlow>
<s:TextFlow>
<s:p>
<s:span>Some text before</s:span>
<s:img id="myImg" source="myImg.png" />
<s:span>Some text after</s:span>
</s:p>
</s:TextFlow>
</s:textFlow>
</s:RichEditableText>
And you listen for mouseOver events on the entire textcomponent.
Then to test if the mouse is over your image, implement the mouseOver handler:
private function toggleTooltip():void {
var graphic:DisplayObject = myImg.graphic;
var anchor:Point = graphic.localToGlobal(new Point(0, 0));
if (mouseX >= anchor.x && mouseX <= anchor.x + graphic.width &&
mouseY >= anchor.y && mouseY <= anchor.y + graphic.height)
{
trace('show tooltip');
}
else {
trace('hide tooltip');
}
}

Resources