I was trying to use tick function as described in the below link https://github.com/aframevr/aframe/blob/master/docs/introduction/best-practices.md but it didnt worked out
AFRAME.registerComponent('foo', {
tick: function () {
var el = this.el;
var rotationTmp = this.rotationTmp = this.rotationTmp || {x: 0, y: 0, z: 0};
var rotation = el.getAttribute('rotation');
rotationTmp.x = rotation.x + 0.1;
rotationTmp.y = rotation.y;
rotationTmp.z = rotation.z;
el.setAttribute('rotation', rotationTmp);
}
});
Expected output the object should rotate around x actual result nothing happens..
I hope it's okay if i clarify a bit Kevin's answer.
But first of all, the code is working. Both in aframe 0.9.2 and 0.8.2.
The issue may be related to
the script being placed after the scene is loaded
the component not being attached to the entity:
js:
AFRAME.registerComponent('foo', {
// component body
})
html:
<a-entity foo></a-entity>
As for Kevins answer - it may be a good idea to make changes in the tick function with referring to the underlying THREE.js object.
If you want to rotate a box, there's no need to throw in all that logic you have:
1) create a tmp variable
2) get the object rotation
3) set the tmp variable
4) set the object rotation
When you have a function which executes on each render loop (tick) then you want to minimize what's happening there. So Kevin directly accesses the THREE.js object, and changes the rotation:
tick: function() {
el.object3D.rotation.x += 0.1 * Math.PI / 180 // three uses radians
}
Check it out in this fiddle.
You can just do el.object3D.rotation.x += 0.1
Related
This event seems to be missing from the standard events. Lick click, I would need to set up a listener for it in a component.
Thanks to Piotr for getting me on the right track. As of this post, there is no double click event for aframe, but we can use js/jquery to get around this. Here is the start of my function:
el.addEventListener('click', function(evt) {
\$('#myEmbeddedSce').on('dblclick', function(event) {
event.stopPropagation();
event.stopImmediatePropagation();
.....
The key was to add the jquery listener inside the aframe listener, and add the stop propagations so the doubleclicks only registered once.
use stopPropagation() to detect the double click in the frame.
The stopPropagation() method prevents propagation of the same event from being called. Propagation means bubbling up to parent elements or capturing down to child elements.
Syntax
event.stopPropagation()
I've solved the issue of detecting by taking tips from existing answers. Ie, tracking the difference of time and distance between clicks. For whatever reason the click listener did not give me clientX and clientY coordinates, so I used the distance vector instead.
This is my solution:
let prevClickTime = Date.now();
let prevVector = new Vector3(0, 0, 0);
// let pointString;
AFRAME.registerComponent('bar', {
init: function () {
this.el.addEventListener('click', (e) => {
const currClickTime = Date.now();
const currVector = e.detail.intersection.point;
const distance = currVector.distanceTo(prevVector);
const timeDiff = currClickTime - prevClickTime;
if (timeDiff < 260 && distance < 0.4) {
console.log('double click');
}
prevClickTime = currClickTime;
prevVector = currVector;
});
},
});
This was pointed at this aframe-react entity.
<Entity
class="interactable"
id="arrow"
bar
geometry={{ primitive: 'cylinder', width: 1, height: 0.05 }}
material={{ color: 'blue', opacity: 0.6 }}
position={`2 0.5 ${adj}`}
rotation="0 0 0"
/>
I'm having trouble finding the right way to set the angular velocity on an object in a-frame.
<script>
AFRAME.registerComponent("cuberotator", {
init: function () {
console.log("trying to rotate the cube...");
const theta = new THREE.Vector3(0, 20, 0);
console.log (theta);
this.el.setAttribute('angularVelocity', theta);
//where is angularVelocity attribute?
}
});
</script>
I've attached a CodePen example at https://codepen.io/mcanterel/pen/LroRYz
Thanks for any insights.
If You want to rotate the cube around a certain point, you'd have to rotate the points frame of reference.
<a-entity>
<a-box></a-box>
</a-entity>
You can move the cube as you wish, and by rotating the wrapper entity, the cube will rotate around its initial center.
Your component can set the rotation in the tick() function which is called each frame.
tick: function() {
let rot = this.el.getAttribute("rotation")
rot.y += this.velocity // increase it on click or whenever
this.el.setAttribute("rotation", rot)
}
I need help in dynamically "highlighting" cities on a world map, created using D3 and geoJSON.
I'm working on a spinning globe with 295 city-markers on it. Every 300 millisec, one of these cities need to "be highlighted", meaning 1) change its color and 2) increase its radius (and then stay that way). This gist shows the visual so far: gist example
Steps taken so far:
1) I started with "circle" elements in d3: highlighting was easily done by changing their class (and using CSS styles) and radius. However: the circles remained visible on the "backside" of the globe...
2) To solve the "no circles on back of earth" problem, this post showed me that JSON paths would help: http://bl.ocks.org/PatrickStotz/1f19b3e4cb848100ffd7.
I have now rewritten the code with these paths, and there is correct clipping of the markers on the back of the earth, but now I am stuck in dynamically accessing the radius and style of each city...
Question about changing the radius:
I understand that using path.pointRadius() I can alter the radius of the city markers. However, I want to do this dynamically (every 300msec), and only on a subselection of the markers each time. And that's where I get stuck...
Question about changing the style:
Also I would like to change the color, but assigning styles to the paths confuses me about how to access the JSON "Point" and "path" elements...
Code snippet showing my failed CSS styles attempt:
svg.append('g')
.selectAll("path")
.data(data,function(d,i){ return d.id })
.enter()
.append("path")
.datum(function(d) {
return {
type: "Point",
coordinates: [d.lon, d.lat],
class: "nohighlight" //MY ATTEMPT AT CHANGING CLASS... Not working...
}; })
.attr("class","city") //this class is assigned to the "correct" paths. Can I access them individually??
.attr("d", pathproj);
Code snippet showing the time loop in which the highlighting needs to happen:
//Highlighting the cities one by one:
var city_idx = 0; //data.id starts at 1
//Every 300 msec: highlight a new city:
var city_play = setInterval(function() {
city_idx++;
var filtered = data.filter(function(d) {
return d.id === city_idx;
});
// CHANGE CLASS?
// CHANGE RADIUS?
//Stop when all cities are highlighted
if(city_idx>=geo_data.length){
clearInterval(city_play) //terminates calls to update function within setInterval function.
};
}, 300); // end timer city play setInterval
Full code in block builder:
blockbuilder - globe and city markers
Please do let me know if I can clarify further!
We can do it this way:
To all path belonging to cities give a class
.selectAll("path")
.data(data,function(d,i){ return d.id })
.enter()
.append("path")
.classed("city", true) <--- so all cities point will have class city
Next in the timer block change radius and class dynamically like this:
var city_play = setInterval(function() {
city_idx++;
// Control the radius of ALL circles!
pathproj.pointRadius(function(d,i) {
//your biz logic
if (i < city_idx){
return 4
} else
return 2
});
// CHANGE CLASS?
// CHANGE RADIUS?
//select all elements with class city
d3.selectAll(".city").attr("class",
function(d, i){
if (i < city_idx){
return "city highlight"
} else
return "city"
}).attr("d", pathproj)
var len = d3.selectAll(".city").data().length;
console.log(city_idx, len)
//Stop when all cities are highlighted
if(city_idx>=len){
clearInterval(city_play) //terminates calls to update function within setInterval function.
};
}, 300);
working code here
I've come through the official docs but wasn't able to locate information about how possibility of zooming in/out panorama images, is it supported in the A-Frame or maybe there is a workaround to read about implementing some of three.js on top of it?
This might be a cleaner way in 2018.
I limited the zoom of the Aframe camera 1-5 so it doesn't get too messy.I just tested this and its working greatly.Hope it helps others.
window.addEventListener("mousewheel", event => {
const delta = Math.sign(event.wheelDelta);
//getting the mouse wheel change (120 or -120 and normalizing it to 1 or -1)
var mycam=document.getElementById('cam').getAttribute('camera');
var finalZoom=document.getElementById('cam').getAttribute('camera').zoom+delta;
//limiting the zoom so it doesnt zoom too much in or out
if(finalZoom<1)
finalZoom=1;
if(finalZoom>5)
finalZoom=5;
mycam.zoom=finalZoom;
//setting the camera element
document.getElementById('cam').setAttribute('camera',mycam);
});
You could either:
Scale an <a-sphere> up or down when detecting the mouse wheel event
zoom in or out the camera, like documented here
This article might be helpful, as it covers using the mousewheel event on multiple browsers.
I think scaling may screw up Your setup, or be a resource waste, so I'd go with 2.
Sandy's answer helped me. I want to contribute an answer which shows the full code and enables smoother zooming (increments of 0.1):
<script>
window.addEventListener("wheel", (event) => {
// small increments for smoother zooming
const delta = event.wheelDelta / 120 / 10;
var mycam = document.getElementById("cam").getAttribute("camera");
var finalZoom =
document.getElementById("cam").getAttribute("camera").zoom + delta;
// limiting the zoom
if (finalZoom < 0.5) finalZoom = 0.5;
if (finalZoom > 2) finalZoom = 2;
mycam.zoom = finalZoom;
document.getElementById("cam").setAttribute("camera", mycam);
});
</script>
<a-scene>
<a-entity
id="cam"
camera="zoom: 1"
look-controls="reverseMouseDrag: true"
></a-entity>
<!-- my pano image stuff -->
<a-assets>
<img id="skyTexture" crossorigin="anonymous" />
</a-assets>
<a-sky src="#skyTexture"></a-sky>
</a-scene>
This is what I put together to do it. Check the initial vrZoom variable.
For me, what I struggled the most, was to understand the way you set a parameter that's inside a component. You have to call it like this: element.setAttribute('componentName', 'parameterName', 'value') and in my case cam.setAttribute('camera', 'zoom', vrZoom)
Here's my script all together. It would be possible to create a component with this, such as look-controls.
var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel";
if (document.attachEvent)
document.attachEvent("on"+mousewheelevt, function(e){scroller(e)});
else if (document.addEventListener)
document.addEventListener(mousewheelevt, function(e){scroller(e)},false);
var vrZoom = 4; // My initial zoom after animation
var cam = document.querySelector('#mainCam');
function scroller(evt)
{
//Guess the delta.
var delta = 0;
if (!evt) evt = window.event;
if (evt.wheelDelta) {
delta = evt.wheelDelta/120;
} else if (evt.detail) {
delta = -evt.detail/3;
}
if (evt.preventDefault) evt.preventDefault();
evt.returnValue = false;
//Actual Zooming.
vrZoom += delta * 0.1
vrZoom = Math.min(Math.max(vrZoom, 1), 8); // clamp between 1 and 8
cam.setAttribute('camera', 'zoom', vrZoom)
}
I struggled quite a bit with getting this to work for an embedded a-frame, especially because the scene would become skewed upon dynamically adjusting the camera's zoom setting. This is a bug with a-frame. Here are the two ways I found to reset the scene upon setting the zoom level.
AFRAME.scenes[0].resize();
Or ...
let scene = document.querySelector('a-scene');
scene.camera.aspect = scene.clientWidth / scene.clientHeight;
scene.camera.updateProjectionMatrix();
I'm working with Flex 3.4 SDK.
I'm trying to programmatically(yep, must be this way) style/skin a VBox so that its top right corner is rounded, and it gets a two colors gradient brackground.
Modifying examples I found around I was able to accomplish both effects(corner and background) but only separately:
VBox with not all rounded corners: http://livedocs.adobe.com/flex/3/html/help.html?content=skinning_6.html
VBox with gradient background: http://butterfliesandbugs.wordpress.com/2007/06/08/generic-background-gradient-for-containers/
But what I need to do is to apply both at the same time. And all my coding attempts so far have failed silently.
Would anyone know how to go about doing this correctly?
I have a post on my blog on how to make this exact component Here.
You create a basic custom MXML component (extending in this case, VBox). You specify a programmatic skin, which is where the bevel and gradient gets applied.
The programmatic skin does all it's drawing in the updateDisplayList function.
Here is some of the code (the rest is on my blog, with a demo)
var g:Graphics = graphics;
var cn:Number = this.getStyle("cornerRadius");
var crtl:Number = this.getStyle("cornerRadiusTopLeft") > 0 ? this.getStyle("cornerRadiusTopLeft") : cn;
var crtr:Number = this.getStyle("cornerRadiusTopRight") > 0 ? this.getStyle("cornerRadiusTopRight") : cn;
var crbl:Number = this.getStyle("cornerRadiusBottomLeft") > 0 ? this.getStyle("cornerRadiusBottomLeft") : cn;
var crbr:Number = this.getStyle("cornerRadiusBottomRight") > 0 ? this.getStyle("cornerRadiusBottomRight") : cn;
var gradFrom:Number = this.getStyle("gradientFrom");
var gradTo:Number = this.getStyle("gradientTo");
var b:EdgeMetrics = borderMetrics;
var w:Number = unscaledWidth - b.left - b.right;
var h:Number = unscaledHeight - b.top - b.bottom;
var m:Matrix = verticalGradientMatrix(0, 0, w, h);
g.clear();
g.beginGradientFill("linear", [gradFrom, gradTo], [1, 1], [0, 255], m);
g.lineStyle(1,borderColor,1,true,LineScaleMode.NORMAL,CapsStyle.ROUND,JointStyle.ROUND);
GraphicsUtil.drawRoundRectComplex(g, b.left, b.top, w, h, crtl, crtr, crbl, crbr);
g.endFill();
}
for a demo, look Here. Hope this helps.
Follow the steps from your second link, but instead of using "drawRect", you should be able to use "drawRoundRectComplex". You may need to play around with some of the matrix settings though. I seem to remember having some problems.
Another option is to use degrafa. There can be a bit of a learning curve, but it's powerful and can do this.