Aframe: Changing glTF models in Aframe using SetAttribute - javascript

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.rawgit.com/tizzle/aframe-orbit-controls-component/v0.1.14/dist/aframe-orbit-controls-component.min.js"></script>
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div id='earthcontainer' style='position:relative; display:flex; width: 100%; justify-content:center ;'>
<a-scene device-orientation-permission-ui='enabled: false' embedded='true' foo loading-screen='enabled:false' renderer='antialias: true; colorManagement: true; sortObjects: true; physicallyCorrectLights: true; maxCanvasWidth: 1920; maxCanvasHeight: 1920;'
style='position:absolute; height: 400px; width: 400px; margin: 0 auto;' vr-mode-ui='enabled: false'>
<a-assets>
<a-asset-item id='earth' src='https://raw.githubusercontent.com/yashrajbharti/kml-images/main/sceneearth/scene.gltf' />
<a-asset-item id='hologram' src='https://raw.githubusercontent.com/yashrajbharti/kml-images/main/scene/scene.gltf' />
</a-assets>
<a-entity camera='fov: 80; zoom: 1;' id='camera' mouse-cursor='' orbit-controls=' enableZoom: false; autoRotate: true; target: #target; enableDamping: true; dampingFactor: 0.125; rotateSpeed:0.25; minDistance:3; maxDistance:100; '
position='0 0 0'>
</a-entity>
<a-entity animation-mixer='loop: repeat' cursor='fuse: true; fuseTimeout: 500' gltf-model='#earth' id='target' position='0 0 -27.6' scale='0.4 0.4 0.4' the-target='true' />
<!-- <a-entity animation-mixer='loop: repeat' gltf-model='#hologram' id='target' position='0 0 -7.6' scale='2.4 2.4 2.4'/>-->
</a-scene>
</div>
<button class="cybr-btn">
Buttons<span aria-hidden>_</span>
<span aria-hidden class="cybr-btn__glitch">Buttons_</span>
</button>
<script>
AFRAME.registerComponent('foo', {
init: function() {
const modelList = ['earth', 'hologram']
const scaleList = ['0.4 0.4 0.4', '2.4 2.4 2.4']
const positionList = ['0 0 -27.6', '0 0 -7.6']
const model = document.getElementById("target")
const changeButton = document.querySelector(".cybr-btn")
let idx = 1 // Start with the 2nd model as 1st is already in the scene on page load
const changeModel = () => {
model.removeAttribute("gltf-model")
// Switch to next model in the list
model.setAttribute("gltf-model", `#${modelList[idx]}`)
// Change Position and Scale
model.setAttribute('position', `${positionList[idx]}`)
model.setAttribute('scale', `${scaleList[idx]}`)
idx = (idx + 1) % modelList.length
}
changeButton.onclick = changeModel
}
})
</script>
</body>
</html>
Using this solution I am able to change models in Aframe with the click of a button. Problem comes when I want to change the scale and position parameters as well.
The code to reproduce the error is added below. It results in an unexpected way of scaling and position of the glTF models.
I think I understand why this problem arise. It's because it is scaling and positioning the a-entity whichever is available, before or after the glTF parameter gets modified. Only thing is how to I solve this. I was also trying the .then{value=>} promise but it didn't help the case.

Related

Set an element attribute multiple times within a javascript function

im using Vanilla Javascript, and my problem is that i need to render a progress bar in a certain time, but JS only render the final step...
'use strict'
var button = document.getElementById('button');
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function initProgressBar()
{
let progressBar = document.querySelector('#progress-bar'),
count = 25,
total = 64;
progressBar.style = `width:${count}%;`;
while (total > 0) {
sleep(300);
total = Math.trunc(total/2);
count = count + total;
progressBar.style = `width:${count}%;`;
}
}
button.onclick = initProgressBar;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#progress-container {
height: 4px;
width: 100%;
}
#progress-bar {
width: 0%;
height: 100%;
background-color: blueviolet;
}
</style>
</head>
<body>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<button id=button>press</button>
<script src='prueba.js'></script>
</body>
</html>
The idea is that when the user clicks the button, the progress bar automatically jumps to 25% and then progressively goes to ~ 88% (after that I'll send it to 100% using another function)
The problem is that Javascript only update the element on his last value (88%).
So, is there any way to set the attribute of an element multiple times inside a javasciript function?
The source of the problem is likely the total = Math.trunc(total/2); construction. You're dividing by 2 and then rounding down.
So it's goal is preset to 25 + 64 = 89, meaning 89% is the end.
Once it gets to 88%, it takes that remaining 1, divides it by 2 (1 / 2 = 0.5) and then it rounds that down, so it becomes 0, 88 + 0 = 88, which still isn't 89, so it'll just end up in an infinite loop there.
The way sleep will block the execution thread and might lead to unexpected behaviour in some cases. A better implementation of sleep could be done using Promise and setTimeout.
Here's the snippet using the modified sleep, that works as expected :
'use strict'
var button = document.getElementById('button');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function initProgressBar()
{
let progressBar = document.querySelector('#progress-bar'),
count = 25,
total = 64;
progressBar.style = `width:${count}%;`;
while (total > 0) {
await sleep(300);
total = Math.trunc(total/2);
count = count + total;
progressBar.style = `width:${count}%;`;
}
}
button.onclick = initProgressBar;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#progress-container {
height: 4px;
width: 100%;
}
#progress-bar {
width: 0%;
height: 100%;
background-color: blueviolet;
}
</style>
</head>
<body>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<button id=button>press</button>
<script src='prueba.js'></script>
</body>
</html>

Aframe issues with interactable components and a random function

I'm trying to create a entity in my Aframe's scene that once clicked bring me in another of my random pages.
I'm a newbie, so my code is a patchwork that sometimes works and other times doesn't. I used this addon (https://github.com/silverslade/aframe_blender_exporter) to translate my 3d Blender scene in an "Aframe" page. It worked very well and this is my result with added javascript lines
<head>
<title>WebXR Application</title>
<link rel="icon" type="image/png" href="index/favicon.ico"/>
<meta name="description" content="3D Application">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras#v6.1.0/dist/aframe-extras.min.js"></script>
<script type="text/javascript" src="index/js/webxr.js"></script>
<script type="text/javascript" src="index/js/joystick.js"></script>
<script type="text/javascript" src="index/js/camera-cube-env.js"></script>
<script>
AFRAME.registerComponent('navigate-on-click', {
schema: {
url: {default: ''}
},
init: function () {
var data = this.data;
var el = this.el;
el.addEventListener('click', function () {
window.location.href = data.url;
});
}
});
</script>
<script>
var urls = [
"http://www.findyourface.it/AuroraReinhard/AuroraReinhard.html",
'http://www.findyourface.it//NikideSaintPhalle/NikideSaintPhalle.html',
'http://www.findyourface.it/AmeliaEarthart/AmeliaEarthart.html'
];
function goSomewhere() {
var url = urls[Math.floor(Math.random()*urls.length)];
window.location = url; // redirect
}
</script>
<link rel="stylesheet" type="text/css" href="index/style.css">
</head>
<body onload="init();">
<a-scene shadow="type: basic; autoUpdate: false;">
<!-- Assets -->
<a-assets>
<a-asset-item id="Plane" src="./index/assets/Plane.gltf"></a-asset-item>
<a-asset-item id="segnalefreccia" src="./index/assets/segnalefreccia.gltf"></a-asset-item>
<img id="sky" src="./index/resources/sky.jpg">
<img id="icon-play" src="./index/resources/play.png">
<img id="icon-pause" src="./index/resources/pause.psng">
<img id="icon-play-skip-back" src="./index/resources/play-skip-back.png">
<img id="icon-mute" src="./index/resources/mute.png">
<img id="icon-volume-low" src="./index/resources/volume-low.png">
<img id="icon-volume-high" src="./index/resources/volume-high.png">
</a-assets>
<!-- Entities -->
<a-entity id="#Plane" gltf-model="#Plane" scale="1 1 1" position="0.0 0.0 -0.0" visible="true" shadow="cast: false" ></a-entity>
<a-entity animation-mixer id="#segnalefreccia" gltf-model="#segnalefreccia" scale="0.25 0.25 0.25" position="0.0 0.0 -10.0" visible="true" shadow="cast: false" onClick="goSomewhere(); return false;" ></a-entity>
<!-- Camera -->
<a-entity id="player"
position="0 -0.2 0"
movement-controls="speed: 0.10000000149011612;">
<a-entity id="camera"
camera="near: 0.001"
position="0 1.7000000476837158 0"
look-controls="pointerLockEnabled: true">
<a-entity id="cursor" cursor="fuse: false;" animation__click="property: scale; startEvents: click; easing: easeInCubic; dur: 50; from: 0.1 0.1 0.1; to: 1 1 1"
position="0 0 -0.1"
geometry="primitive: circle; radius: 0.001;"
material="color: #CCC; shader: flat;"
>
</a-entity>
</a-entity>
<a-entity id="leftHand" oculus-touch-controls="hand: left" vive-controls="hand: left"></a-entity>
<a-entity id="rightHand" laser-controls oculus-touch-controls="hand: right" vive-controls="hand: right" ></a-entity>
</a-entity>
<!-- Lights -->
<a-entity light="intensity: 10; shadowBias: -0.001; shadowCameraFar: 501.02; shadowCameraBottom: 12; shadowCameraFov: 101.79; shadowCameraNear: 0; shadowCameraTop: -5; shadowCameraRight: 10; shadowCameraLeft: -10; shadowRadius: 2; angle: 180; decay: 1.3; type: spot; distance: 4.41; penumbra: 0.01" position="0 3.53829 -9.8453"></a-entity>
<!-- Sky -->
<a-sky color="#000000"></a-sky>
</a-scene>
</body>
The first time you open the scene you have to click on it a lot of times but it eventually works, but in the following pages it stops working altogether. I think it’s because this is a Frankenstein monster of a code, but I didn't find a better way to make it.
Thank you for helping!

In AR.js When Load Model ,Show A Loading Screen

When We Load A Big 3D Model Or Big A Video, It Takes Time To Load The Assets(Resources) And Render The Resources,So I Want To Show A Loading Screen Or Loading Gif File or a Loading A-Box - Till The Whole Assets Loading And Rendering Is Done,On The Screen Or On The Pattern.Please Go Through My GLITCH , Its Working But It Will Take 10-15 Sec to load and render.
I tried To Add Asset Loading Manager But It didn't Worked . I Tried All The Ways But It Didn't Worked
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello!</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script type="text/javascript" src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.7.2/aframe/build/aframe-ar.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v6.0.0/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/aframe-animation-component#5.1.2/dist/aframe-animation-component.min.js"></script>
</head>
<body>
<a-scene vr-mode-ui="enabled: false" artoolkit='sourceType: webcam; detectionMode: mono; maxDetectionRate: 90;' arjs='debugUIEnabled: false;detectionMode: mono_and_matrix; matrixCodeType: 3x3;'>
<a-assets>
<a-entity src="https://cdn.glitch.com/a5ca03d3-3024-44dc-9256-0b75c4247613%2Fscene.gltf?v=1563255364433" id="model"></a-entity>
</a-assets>
<a-marker preset="hiro">
<a-entity gltf-model="#model" material='transparent:true;shader:flat;side:double;metelness:2' scale="0.04 0.04 0.04" ></a-entity>
</a-marker>
<a-entity camera></a-entity>
<a-entity shadow="cast: true"></a-entity>
</a-scene>
<script>
// Workaround for an AR.js bug (https://github.com/jeromeetienne/AR.js/issues/410)
const sceneEl = document.querySelector('a-scene');
sceneEl.addEventListener('loaded', () => {
sceneEl.camera = new THREE.PerspectiveCamera();
scene.add( camera );
});
</script>
</body>
</html>
After Showing The HIRO Pattern Its Takes 10-15 Seconds (Depending Upon The Internet Speed) To Load And Render . I Want To Show Some Preloader Or Loading Screen Or Some Gif Loading Image To Display Till The Object (Assets) Loads Completely And Render And Disappear Once The Rendering And Loading Is Done......
Thanks In Advance
For models, you can use the model-loaded event:
<a-marker preset="hiro">
<a-entity id='loadingEl'></a-entity>
<a-entity gltf-model="#model"></a-entity>
</a-marker>
The #loadingEl could have a primitive box with a "loading" image, and when the model is loaded, you remove (the entity or its visibility):
AFRAME.registerComponent('foo', {
init: function() {
this.el.addEventListener('model-loaded', e => {
document.querySelector("#loadingEl").remove()
})
}
})
Glitch here.
The <a-assets> system have a loaded event emitted, you could use it for videos as well.
This is a fix for Aryan's glitch, use the Hiro marker and your 3d model - Glitch
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello!</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script type="text/javascript" src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/jeromeetienne/AR.js/1.7.2/aframe/build/aframe-ar.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v6.0.0/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/aframe-animation-component#5.1.2/dist/aframe-animation-component.min.js"></script>
<script>
AFRAME.registerComponent('loader', {
init: function() {
let loader = document.querySelector("#loadingEl")
this.el.addEventListener('model-loaded', e => {
console.log('loaded')
loader.setAttribute("visible", "false")
})
}
})
</script>
</head>
<body>
<a-scene vr-mode-ui="enabled: false" artoolkit='sourceType: webcam; detectionMode: mono; maxDetectionRate: 90;' arjs='debugUIEnabled: false;detectionMode: mono_and_matrix; matrixCodeType: 3x3;'>
<a-assets>
<a-entity src="https://cdn.glitch.com/a5ca03d3-3024-44dc-9256-0b75c4247613%2Fscene.gltf" id="model"></a-entity>
</a-assets>
<a-marker preset="hiro">
<a-box id="loadingEl"></a-box>
<a-entity gltf-model="#model" material='transparent:true;shader:flat;side:double;metelness:2' scale="0.04 0.04 0.04" loader></a-entity>
</a-marker>
<a-entity camera></a-entity>
<a-entity shadow="cast: true"></a-entity>
</a-scene>
<script>
// Workaround for an AR.js bug (https://github.com/jeromeetienne/AR.js/issues/410)
const sceneEl = document.querySelector('a-scene');
sceneEl.addEventListener('loaded', () => {
sceneEl.camera = new THREE.PerspectiveCamera();
scene.add( camera );
});
</script>
</body>
</html>

Rotate image via Range Slider

Hi guys how to make the image rotate via range slider?
I built the function so it can range between 0 and 360 and show the value range, it's working fine, but how to apply this to rotate the image?
The crop image script documentation is here
I updated the code with the crop script, please help to rotate the image and output to a div
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Cropper.js</title>
<!-- <link rel="stylesheet" href="dist/cropper.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.css" />
<style>
.container {
max-width: 640px;
margin: 20px auto;
}
img {
max-width: 100%;
}
</style>
</head>
<body>
<div class="container">
<h1>Cropper with a range of aspect ratio</h1>
<div>
<img id="image" src="https://fengyuanchen.github.io/cropperjs/images/picture.jpg" alt="Picture">
</div>
<button onclick="cropper.getCroppedCanvas()">Save</button>
</div>
<!-- <script src="dist/cropper.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
var image = document.querySelector('#image');
var minAspectRatio = 1.0;
var maxAspectRatio = 1.0;
var cropper = new Cropper(image, {
ready: function () {
var cropper = this.cropper;
var containerData = cropper.getContainerData();
var cropBoxData = cropper.getCropBoxData();
var aspectRatio = cropBoxData.width / cropBoxData.height;
var newCropBoxWidth;
if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio) {
newCropBoxWidth = cropBoxData.height * ((minAspectRatio + maxAspectRatio) / 2);
cropper.setCropBoxData({
left: (containerData.width - newCropBoxWidth) / 2,
width: newCropBoxWidth
});
}
},
cropmove: function () {
var cropper = this.cropper;
var cropBoxData = cropper.getCropBoxData();
var aspectRatio = cropBoxData.width / cropBoxData.height;
if (aspectRatio < minAspectRatio) {
cropper.setCropBoxData({
width: cropBoxData.height * minAspectRatio
});
} else if (aspectRatio > maxAspectRatio) {
cropper.setCropBoxData({
width: cropBoxData.height * maxAspectRatio
});
}
}
});
});
</script>
<script>
function updateTextInput(val) {
document.getElementById('textInput').value=val;
}
</script>
<input type="range" name="rangeInput" min="0" max="360" onchange="updateTextInput(this.value);">
<input type="text" id="textInput" value="">
<!-- FULL DOCUMENTATION ON https://github.com/fengyuanchen/cropperjs -->
</body>
</html>
You can use some javascript to set the rotation of the image when the slider value changes. Since you have jQuery:
$(function(){
let slider = $('input[type=range]'),
image = $('#image');
slider.on('change mousemove', function(){
image.css('transform', 'rotate(' + $(this).val() + 'deg)');
});
});
Side note: This type of event assignment - finding the element in javascript, rather than adding onchange attributes to the input - is much more flexible and maintainable.
Here's an example: https://codepen.io/benjamin-hull/pen/ewxboE
A couple of things to watch:
I've added the 'mousemove' event listener as well as 'change', so the user gets real-time feedback as they move the slider. This might be a problem, as mousemove can produce 100's of events. You might need to look at 'debouncing' that event to limit it to a sensible value.
In my example, I've set the slider to min -180, max 180 and a default of 0. This allows the user to rotate left and right.
You probably want the rotation to scale the image as well, so it always fills the frame.

javascript teechart curved line issue

I want to draw smoothed lines for chart.
I have already made a chart using teechart.js, but it is not good working.
I've attached an image. Left image is mine.
I'd like to make line such as right
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Chart</title>
<script src="./js/three.min.js" type="text/javascript"></script>
<script src="./js/Detector.js" type="text/javascript"></script>
<script src="./js/TrackballControls.js" type="text/javascript"></script>
<script src="./js/helvetiker_regular.typeface.js"></script>
<script src="./js/teechart.js" type="text/javascript"></script>
<script src="./js/teechart-3d.js" type="text/javascript"></script>
<script type="text/javascript">
"use strict";
var three1, Chart1;
function draw() {
three1 = new Tee.Three("canvas1");
three1.setShowShadows(true);
Chart1 = new Tee.Chart(three1);
Chart1.addSeries(new Tee.Line([60,70,70,60,50,40,30,40,50,60,50,40] , 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(',')));
Chart1.addSeries(new Tee.Line([20,30,40,50,60,50,40,50,60,70,70,60]));
Chart1.title.text="";
Chart1.footer.text="";
Chart1.legend.visible = false;
Chart1.walls.back.size=0.2;
Chart1.walls.left.size=10;
Chart1.walls.bottom.size=10;
Chart1.walls.back.format.transparency=0.2;
Chart1.bounds.x = -100;
Chart1.bounds.y = 50;
Chart1.bounds.width = 900;
Chart1.bounds.height = 400;
Chart1.axes.left.setMinMax(0, 120);
if (three1.isEnabled()) {
Chart1.draw();
animate1();
} else {
// Show message (WebGL not available) :
Detector.addGetWebGLMessage();
// Disable WebGL and use HTML5 2D Canvas:
three1.setEnabled(false, Chart1);
}
// Loop
function animate1() {
three1.update();
requestAnimationFrame(animate1);
}
}
</script>
</head>
<body onload="draw()">
<canvas id="canvas1" style="width: 700px; height: 500px; display: inline; background: white;" width="800" height="500"></canvas>
</body>
</html>
Hope good answers.
Checking your code I see it misses a couple of details, but correcting them it works without problems:
Set smoothed lines. In your example it would be:
Chart1.series.items[0].smooth = 0.5;
Chart1.series.items[1].smooth = 0.5;
Include teechart-extras.js to define Tee.drawSpline. It would be something like this:
<script src="./js/teechart-extras.js" type="text/javascript"></script>
I think you should take a look at THREE.TubeGeometry where you can pass a spline/curve as a path for the tube:
extrudePath = // define some spline or curve;
segments = 64;
radius = 5;
radiusSegments = 8;
closed = false;
tube = new THREE.TubeGeometry( extrudePath, segments, radius, radiusSegments, closed );
mesh = new THREE.Mesh( tube, material );
scene.add( mesh );
Check also the examples here

Categories