i've been searching for this the last couple of weeks and i can't find an answer.
This is what i'm trying to accomplish ! Click to see the image.
I need to scroll through an infinite number of apps, on a touchscreen device.
The one in the middle needs to be bigger, and the rest smaller and smaller, to give the idea that there are more apps that are not visible and you need to touch and scroll though apps.
I'm trying to do this with Jquery, HTML5 and CSS3.
Can anybody help me, please?
I'm really stuck here..
Thank you very much!
I tried using this http://jsfiddle.net/67zMe/5/
But i had no luck making it semi-circular and based on touch screen scrolling action.
var radius = 300;
var angleSteps = 360 / $('#circle-list li').length;
var baseAngle = 0;
function updateListPositions()
{
$('#circle-list li').each(function(index, element)
{
var angle = baseAngle + (index * angleSteps);
var center = 150;
var distance = 100;
var x = distance * Math.cos(angle * (Math.PI / 180));
var y = distance * Math.sin(angle * (Math.PI / 180));
$(element).css({left:center+x, top:center+y});
});
}
var stepInterval = setInterval(stepAngle, 25);
function stepAngle()
{
baseAngle++;
updateListPositions();
}
Here is the javascript. It's the closest thing i found, to what i need.
The guy who posted this, had the same problem, and didn't find the answer he needed.
Related
My Three.js project uses and OrthographicCamera and OrthographicTrackBallControls for zoom/pan. I'm trying to add functionality to zoom to the cursor position with no luck. First things first, here's how I'm getting mouse position:
var mX = ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
var vector = new THREE.Vector3(mX, mY, 0.5);
vector.unproject(camera);
vector.sub(camera.position);
Through looking on StackOverflow, there seems to be a lot of information on how to do this with PerspectiveCamera, but these methods don't work with OrthographicCamera. I was able to find this example:
https://htmlpreview.github.io/?https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html
Which does precisely what I am trying to accomplish, but the code that achieves this is hidden, though I am able to discern that the camera position is being changed.
Another SO question which is similar suggests changing camera.left, camera.right, camera.top and camera.bottom, but I have had no luck with this approach. This approach seems like a possibility, but I dont understand the calculations necessary to get the correct left, right, top and bottom values.
So the way I see it I have two possibilities:
Change camera's left/right/top/bottom to get the correct view rectangle.
Change camera position.
But I don't know how to get the values I need to accomplish either, let alone which is the better approach.
UPDATE 11/16/2018:
I've updated my function to this ( based on https://github.com/w3dot0/three.js/blob/973bf1d40ef552dbf19c19654a79f70e2882563d/examples/misc_controls_zoom_to_mouse.html):
zoomDirection = new THREE.Vector3();
function mousewheel(event) {
event.preventDefault();
var amount = event.deltaY / 100;
var zoom = camera.zoom - amount;
var offset = el.offset();
;
var mX = amount > 0 ? 0 : ((event.clientX - offset.left) / renderer.domElement.clientWidth) * 2 - 1;
var mY = amount > 0 ? 0 : -((event.clientY - offset.top) / renderer.domElement.clientHeight) * 2 + 1;
zoomDirection.set(mX, mY, 0.001)
.unproject(camera)
.sub(camera.position)
.multiplyScalar(amount / zoom);
camera.position.subVectors(camera.position, zoomDirection);
orthographictrackBallControls.target.subVectors(orthographictrackBallControls.target, webGl.zoomDirection);
camera.zoom = zoom;
camera.updateProjectionMatrix();
}
This seems to work at first: the camera zooms into the mouse point, but then the camera starts to "jump" around after a bit of zooming, with the mesh no longer visible on screen.
Something that might help: I have an axis helper in the screen as well that "flips" when it stops working as expected. When the scene is loaded, the X-axis helper point due left, but when I get to the point where the camera jumps and I no longer see the mesh, the X-axis helper flips to point due right.
Also, if I zoom OUT first, I can zoom in further before the mesh disappears. I'm not sure what this all adds up to but I would appreciate any help.
First week back after New Year and it's taken too long to fix this. Six sides of A4 covered with linear algebra results in
if ( factor !== 1.0 && factor > 0.0 ) {
const mX = (event.offsetX / event.target.width ) * 2 - 1;
const mY = -(event.offsetY / event.target.height) * 2 + 1;
const vector1 = new THREE.Vector3(mX, mY, 0);
const vector2 = new THREE.Vector3(0, 0, 0);
vector1.unproject(this.camera);
vector2.unproject(this.camera);
vector1.subVectors(vector1, vector2);
this.camera.zoom /= factor;
vector1.multiplyScalar(factor - 1.0);
this.camera.position.subVectors(this.camera.position, vector1);
this.controls.target.subVectors(this.controls.target, vector1);
this.camera.updateProjectionMatrix();
this.camera.updateMatrix();
}
Note the different calculation of mX, mY so that it is valid for a viewport.
Implementing the D3-library with its zoom function may seem like a good idea for this case. But giving up the three-controls is in a lot of cases not a deal.
If you want a zoom-behavior like in Google Maps, the following code could be helpful:
const cameraPosition = camera.position.clone();
// my camera.zoom starts with 0.2
if (zoomOld !== 0.2) {
const xNew = this.curserVector.x + (((cameraPosition.x - this.curserVector.x) * camera.zoom) /zoomOld);
const yNew = this.curserVector.y + (((cameraPosition.y - this.curserVector.y) * camera.zoom) /zoomOld);
const diffX = cameraPosition.x - xNew;
const diffY = cameraPosition.y - yNew;
camera.position.x += diffX;
camera.position.y += diffY;
controls.target.x += diffX;
controls.target.y += diffY;
}
zoomOld = camera.zoom;
Your other problem could be caused by the frustum. But I don't know, I'm still a newbie with Three xD
I'm making AR Mobile Web App, and my current goal is to show some events on the camera screen. For example, if there is fire in direction north from me, I want to point phone to north, and fire logo should be visible there.
My problem is - how can I get the absolute compass data?
I've tried this JavaScript code:
function compassHeading(alpha, beta, gamma) {
var dataContainerMotion = document.getElementById('dataContainerMotion');
// Convert degrees to radians
var alphaRad = alpha * (Math.PI / 180);
var betaRad = beta * (Math.PI / 180);
var gammaRad = gamma * (Math.PI / 180);
// Calculate equation components
var cA = Math.cos(alphaRad);
var sA = Math.sin(alphaRad);
var cB = Math.cos(betaRad);
var sB = Math.sin(betaRad);
var cG = Math.cos(gammaRad);
var sG = Math.sin(gammaRad);
// Calculate A, B, C rotation components
var rA = - cA * sG - sA * sB * cG;
var rB = - sA * sG + cA * sB * cG;
var rC = - cB * cG;
// Calculate compass heading
var compassHeading = Math.atan(rA / rB);
// Convert from half unit circle to whole unit circle
if(rB < 0) {
compassHeading += Math.PI;
}else if(rA < 0) {
compassHeading += 2 * Math.PI;
}
// Convert radians to degrees
compassHeading *= 180 / Math.PI;
return compassHeading;
}
window.addEventListener('deviceorientation', function(evt) {
heading = compassHeading(alpha, beta, gamma);
dataContainerMotion.innerHTML = heading;
}, false);
But variable absolute shows false, and I can't get absolute value. Compass is calibrated according to the positioning of the phone on initialization of the page - not according to actual position of the North on the Earth.
I would appreciate any kind of help - I'm stuck here and I can't go further without getting the compass data.
You have not mentioned the mobile device you have tested with. There are some major "problems" you have to consider:
Alpha on iOS (iPhone, iPad) devices has the value = 0 from where you
start/activate the deviceorientation event. If the phone is currently
heading to East then Alpha is 0 at East. If your phone is heading to
W then Alpha is 0 at West, etc.
There is no way to access the implemented hardware compass in Android
devices (by Javascript). Some browsers (versions) handle the
calculated values alpha, beta and gamma different, so you might have
to add/distract a specific angle value. This is also mentioned in the
MDN Web Docs
Chrome (50+) supports the event "ondeviceorientationabsolute" that
might your work easier (at least for Chrome)
Solution for iOS devices:
window.addEventListener('deviceorientation', function(event) {
if (event.webkitCompassHeading) {
// You may consider adding/distracting landscape/portrait mode value here
alpha = event.webkitCompassHeading;
if (alpha < 0) { alpha += 360; }
if (alpha > 360) { alpha -= 360; }
}
}
Solution for Android devices:
Use alpha/beta/gamma values (as you did). There are already many good solutions around. The "best" (imho) can be found here represented by the TILT compass: mentioned here or directly on GitHub
Considering your code (I haven't checked in detail) is that it won't working properly on iOS devices and probably not even on all Android devices. So all over use a construct like Frosty Z mention in the link posted above.
Sadly there is no 100% reliable code around covering all types of devices and browsers. I still hope something written here might help.
I wish to create the compass / arrow exactly like the one we see in AroundMe Mobile App that point exactly to a pin to the map accordingly with my mobile position and update the arrow when I move the phone.
I'm getting crazy to understand exactly how to do that and I can not find any guide or tutorial that explain a bit it.
What I found online is a bearing function and I created a directive around it:
app.directive('arrow', function () {
function bearing(lat1, lng1, lat2, lng2) {
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
var brng = toDeg(rad);
return (brng + 360) % 360;
}
function toRad(deg) {
return deg * Math.PI / 180;
}
function toDeg(rad) {
return rad * 180 / Math.PI;
}
return {
restrict: 'E',
link: function (scope, element, attrs) {
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
}
};
});
It seems to update the arrow in a direction but unfortunately it is not the right direction because it is not calculated using also the mobile magneticHeading position.
So I added the ngCordova plugin for Device Orientation to get the magneticHeading and now I don't know exactly how to use it and where in the bearing function.
$cordovaDeviceOrientation.getCurrentHeading().then(function(result) {
var magneticHeading = result.magneticHeading;
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
});
I tried to add it in the return statement:
return (brng - heading) % 360;
or:
return (heading - ((brng + 360) % 360));
Implementing this code with a watcher I see the arrow moving but not in the exact position... For example from my position and the pin the arrow should point to N and it is pointing to E.
Always looking online I can not find any tutorial / question to find the bearing between a lat/lng point and a magnetingHeading.
Maybe I'm close to the solution but I can not go ahead alone.
I also tried to search for a mathematical formulas but even there is a huge pain to understand and implement it.
I hope you can help.
It's hard to give a plain answer to this question because a lot depends on the actual graphical representation. For instance, in what direction do you point where rotate(0deg).
I can explain the formula you've found, which might help you to clear the issue yourself. The hard part is the following:
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
What you see here is Haversines formula (https://en.wikipedia.org/wiki/Haversine_formula). Normally one could suffice with two sets of coordinates and calculate the angle between them. When working with latitude and longitude this will not work because the earth is not a flat surface. The first three lines are Haversine and the result (x and y) are coordinates on the unit circle (https://en.wikipedia.org/wiki/Unit_circle)
The next step is to calculate the angle from the point on this unit circle to the center. We can use the arctangant for this. Javascript has a helper function atan2 which simplifies this process. The result is simple the angle of your point towards the center of the circle. In other words, your position towards your point of interest. The result is in radians and needs to be converted to degrees. (https://en.wikipedia.org/wiki/Atan2)
A simplified version with a flat coordinate system would look like this:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
Where poi is the Point of Interest and you is your position.
To compensate for your own rotation, you need to substract your own rotation. In the above sample the result would become:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
rotation -= you.rotation;
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
I've made a Plunckr in which you can see a simple flat coordinate system. You can move and rotate you and the point of interest. The arrow inside 'you' will always point towards the poi, even if you rotate 'you'. This is because we compensate for our own rotation.
https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview
Note in the Plunckr that the 'zero'-position is always to the north. Check your app to see your 'zero'-position. Adjust it accordingly and your script will work.
Hope this helps :-)
JSFiddle link - http://jsfiddle.net/lustre/p0z9wp51/1/
I didn't create this code, but the blog which it was created from isn't working at the moment...
Is it possible to have the spinner center itself on the segment it lands on? For example, sometimes it may just land on segment "A", could it then shift to center itself on segment "A"? Or would that require a complete rebuild of the spinner itself?
It's worth noting that the number of segments is dynamic, in that users can add their own names.
The code below begins the spin, and calculates the angle, and total time to spin.
spin: function() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
},
And this code actually rotates the wheel (afaik).
var rotateWheel = function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawWheel();
spinTimeout = setTimeout(rotateWheel, 30);
}
index returns the segment/array index of the section it's landed on.
outterRadius is the radius of the outer circle. Responsively, the circle is resized with CSS percentages, would this affect the outer radius in HTML5? Or not as it's rendered before the CSS takes effect?
If it would require a complete rebuild, that's fine. Just looking for feedback on whether it's even possible with this code from people more experience in HTML5 than me :)
I have 100 items entering the viewport in random order. Together, they need to form a circle inside a DOM container. I need some way to calculate the position the items need to move to...
The structure is kinda like this:
http://codepen.io/anon/pen/WvbKjb (visual sample with a bit of css and js inside)
<div id="circle"><!-- 100 items in here --></div</div>
And then the JS, for this sample, would generate 100 divs and set their position with css:
for (i = 0; i <= 100; i++) {
var item = document.createElement('div');
item.id = 'item'+i;
item.className = 'item';
item.setAttribute('style', 'left:0px;top:0px');
document.getElementById('circle').appendChild(item);
}
So I would generate 100 .item elements and move them around the screen. This movement is not an issue: what I don't know how to do is find the position each item has to end up at to properly fill the circle. Is there any way to easily calculate this with a formula? I'm afraid it's way beyond my math skills...
Thanks in advance for any help.
EDIT: using jQuery would be fine too.
You may start with this, just a bit of a math:
Example
the main part is:
var spacing = 360 / count;
var l = Math.cos(rotation * Math.PI / 180) * radius - itemRadius;
var t = Math.sin(rotation * Math.PI / 180) * radius - itemRadius;
rotation += spacing;
Where spacing is actually an angle
Probably a little overloaded but this is what i tried and what worked for me: https://jsfiddle.net/tx7po9eg/1/
Main Part is this function which will calculate the position of a specific element depending on the defined center and the radius.
function getPos(cent, rad, amount, iteration) {
var degree = 360 / amount * iteration;
var changeY = cent.y - (Math.sin(degree * Math.PI / 180) * rad);
var changeX = cent.x - (Math.cos(degree * Math.PI / 180) * rad);
var ret = {};
ret.x = Math.round(changeX * 100) / 100;
ret.y = Math.round(changeY * 100) / 100;
return ret;
}
here an example including visualization:
https://jsfiddle.net/tx7po9eg/3/
You will have to decide exactly HOW you want to fit all the elements inside the circle. Do you want them in a single line around the parameter? At Random? Fill the circle?
If you want to fill the circle, one thing you could do is work like that - move from the center outside, starting by putting an item in the middle, then going out and putting another circle around it, then another around it, and like that keep wrapping it in layers until you hit the end of the circle. The only thing to figure out here would be the spacing (e.g. how many circles in each layer), and that would depend on the size of each element (are the sizes fixed)?
If you can make the container circle as large as you desire, you can just start making layers until you run out of items, and then place the big circle around them.
Hope this helps...