aframe examples model-viewer and adding animation handler - javascript

I'm working on a project in which I want to repurpuse this example
https://aframe.io/aframe/examples/showcase/model-viewer/
all I want to add is a trigger that starts the animation on click event.
I have managed to implement this to run on my server
https://github.com/aframevr/aframe/tree/master/examples/showcase/model-viewer
but now struggling to code the event handler.
in model-viewer.js I can see a line that triggers animation at the start
modelEl.setAttribute('animation-mixer', '');
I cant seem to figure out how to play it on click.
I have done this implementation before in a simpler setup (https://codepen.io/wspluta/pen/rNwReNB)
<script>
AFRAME.registerComponent('animationhandler', {
init: function() {
let playing = false;
this.el.addEventListener('click', () => {
if (!playing) {
this.el.setAttribute('animation-mixer', 'clip:*; loop: once; clampWhenFinished: true; duration : 6');
playing=true;
} else {
this.el.removeAttribute('animation-mixer');
playing=false;
}
});
}
})
</script>
<body>
<a-scene>
<a-assets>
<a-asset id="sky" src="https://raw.githubusercontent.com/WSPluta/webxr102/main/tatooine.jpg"> </a-asset>
<a-asset-item id="tie" src="https://raw.githubusercontent.com/WSPluta/webxr102/main/newTie.gltf"></a-asset-item>
</a-assets> <a-entity id="tie" gltf-model="#tie" position="0 0.5 -5" scale="0.25 0.25 0.25" animationhandler></a-entity>
<a-plane id="background" position="0 5 -15" height="9" width="16" rotation="0 0 0" opacity="0.9"></a-plane>
<a-sky src="#sky"></a-sky>
<a-camera>
<a-cursor color="yellow"></a-cursor>
</a-camera>
</a-scene>
</body>
but I'm unable to figure out how to modify example/showcase document in order to implement it. I really want to reuse the camera movement and all the good stuff that comes from the example/showcase file.

I wanted to play an animation and used this code with react/nextjs to achieve this functionality.
const handleAnimate = useCallback(
(e) => {
const { keyCode } = e
const rot = document.getElementById("camOrbit").getAttribute("rotation");
// when w is pressed
if (keyCode === 87) {
document
.getElementById("modAnim")
.setAttribute("animation-mixer", `clip: Walk; loop: repeat`);
document.getElementById("modAnim").setAttribute("rotation", `0 ${rot.y} 0`);
//
//
//
//
// document.getElementById("modAnim").setAttribute("position", `${pos.x} 0 ${pos.z}`);
document.addEventListener("keyup", (event) => {
document
.getElementById("modAnim")
.setAttribute("animation-mixer", `clip: Idle; loop: repeat`);
});
}
},
[setAttribute]
);

Related

A-frame sound : How to react to the end of the sound?

Loop false, poolsize 1.
Running sound.playSound() successfully plays the sound, but sound.isPlaying remains true even after the full sound has been played.
Already tried adding the 'sound-ended' event listener on the entity but it doesn't trigger neither.
There must be a proper state somewhere... right ?
You can track the state of the sound component by listening to the sound-ended event. Below, click the button to play a sound, it will remain red until the audio track is over.
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent("foo", {
init: function() {
const soundComp = this.el.components.sound; // grab the `sound` component
// play the sound when the user clicks the button
this.el.addEventListener("click", evt => {
this.el.setAttribute("color", "red") // turn the button red
soundComp.playSound(); // playsound
})
// catch the `sound-ended` event
this.el.addEventListener("sound-ended", evt => {
this.el.setAttribute("color", "green") // turn it back to green
})
}
})
</script>
<a-scene cursor="rayOrigin: mouse" raycaster="objects: a-sphere">
<a-assets>
<audio id="ding" crossorigin="anonymous" src="https://gftruj.github.io/webzamples/arjs/sound/sound/Ding-sound-effect.mp3" preload="auto"></audio>
</a-assets>
<a-sphere position="0 1 -3" radius="0.25" color="green" sound="src: #ding; autoplay: false" foo ></a-sphere>
</a-scene>

Play audio ONCE on marker detection A-frame & Ar.js

I'm trying to play a sound just once when a marker is detected with the A-frame and AR.JS libraries.
I'm trying the code lines below but the sound is playing indefinite.
<a-scene embedded arjs='sourceType: webcam; debugUIEnabled: false;';>
<a-assets>
<audio id="sound" src="audio.mp3" preload="auto"></audio>
</a-assets>
<a-marker preset="hiro">
<a-entity id="examplemodel" gltf-model="./models/Example.glb" soundhandler></a-entity>
</a-marker>
<a-entity sound="src: #sound" autoplay="false"></a-entity>
<a-entity camera></a-entity>
</a-scene>
<script>
AFRAME.registerComponent('soundhandler', {
tick: function () {
var entity = document.querySelector('[sound]');
if (document.querySelector('a-marker').object3D.visible == true) {
entity.components.sound.playSound();
} else {
entity.components.sound.pauseSound();
}
}
});
</script>
When I changed tick for init I got this error:
Uncaught TypeError: Cannot read property 'playSound' of undefined
Could you please give me some ideas to solve this issue?
It's playing indefinetely, because once it's visible - on each render loop you call playSound().
If you add a simple toggle check - You'll get your "once per visible" result:
tick: function() {
// marker is the object3D of marker
if (marker.visible && !this.markervisible) {
// do your stuff here once per visible
this.markervisible = true;
} else if (!marker.visible) {
this.markervisible = false;
}
}
Check it out here

Using an Object's postion for an Event in AFrame

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)

EventListeners not working with A-Frame parented entity

I'm trying to do one of the most simple things ever, add event listeners to an element in A-Frame. However, I'm new to A-Frame and running into a lot of issues. It's not even that the code isn't rotating the object on mousedown, it's that no clicks are being registered through the console. I figured it may have something to do with the fact that the "fishing-logic" component is added to a parent whose position is (0,0,0) while the part of the model I'm trying to click has a position of (0, 1, -3) but even when I add the "fishing-logic" component to the it still doesn't register clicks. Can anyone help me understand what I'm doing wrong?
AFRAME.registerComponent('fishing-logic', {
init: function() {
var el = this.el;
var isDragging = false;
el.addEventListener('mouseenter', function() {
console.log("Calling mouse enter!");
});
el.addEventListener('mouseleave', function() {
console.log("Calling mouse leave!");
});
el.addEventListener('mousedown', function(evt) {
console.log("Calling mouse down!");
isDragging = true;
if(this.isDragging){
el.object3D.rotation.x += Math.PI;
}
});
el.addEventListener('mouseup', function(evt) {
console.log("Calling mouse up!");
});
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>A-Frame Starter</title>
<meta name="description" content="Base Project">
<script src="https://aframe.io/releases/0.9.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-event-set-component#4.2.1/dist/aframe-event-set-component.min.js"></script>
<script src="//cdn.rawgit.com/donmccurdy/aframe-extras/v6.0.0/dist/aframe-extras.min.js"></script>
<script src="//cdn.rawgit.com/donmccurdy/aframe-physics-system/v3.3.0/dist/aframe-physics-system.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<a-scene>
<a-assets><a-asset-item id="fishing_pole" src="assets/Fishing_Pole_01.gltf"></a-assets>
<a-entity fishing-logic>
<a-gltf-model src="#fishing_pole"
position="0 1 -3"
rotation="0 0 0"
scale="0.1 0.1 0.1"></a-gltf-model>
<a-entity line="start: 0, 3.3, -2.9; end:0 0 -5; color: white"></a-entity>
</a-entity>
<a-sky color="#111133"></a-sky>
<a-plane rotation="-90 0 0" color="#000011" width="64" height="64" depth="64" shadow></a-plane>
</a-scene>
<div class="glitchButton" style="position:fixed;top:20px;right:20px;"></div>
<script src="https://button.glitch.me/button.js"></script>
</body>
</html>
The issue is that because fishing-logic is on a parent a-entity to the a-gtlf-model, it needed an "interactable" query selector/class for the raycaster. This can be seen in the code snippet below. I then added the interactable component alongside the fishing-logic component in the a-entity and clicks are now being registered.
AFRAME.registerComponent('interactable', {
init() {
let el = this.el;
let originalColor = el.getAttribute('material').color;
el.addEventListener('raycaster-intersected', function() {
el.setAttribute('material', 'color', '#24CAFF');
});
el.addEventListener('raycaster-intersected-cleared', function() {
el.setAttribute('material', 'color', originalColor);
});
el.addEventListener('mousedown', function() {
el.body.applyImpulse(
new CANNON.Vec3(0, 3, 0),
new CANNON.Vec3().copy(el.getAttribute('position'))
);
});
}
});

Registered Component click event in Aframe Version 0.8.2 and master isn't working

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.

Categories

Resources