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
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>
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
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'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'))
);
});
}
});
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.