Compass spinning javascript problem with angle - rotate/translate - javascript

i'm building a compass for the iphone, duh, just for experiment purpose and to learn how to do it, in javascript, with phonegap.
now i'm already able to get the degrees, i was able to apply the deg 0-360 to a div (let's call it the "wheel") using the webkit-transform translateZ (or i could use rotate)
but i have a bug:
when the wheel goes from 0deg to 359deg everything is ok, but when the degree goes to 0deg again, instead of smoothly spin in that direction (clockwise), it spin rapidly again anti-clockwise to the positin 0deg...
i dont know i'm clear because is not easy to explain without an example....'
basically my problem is to find the right script to move the wheel starting with the value 0-360 that i can get easily from the iphone.
any suggestions are welcome.

Roll over when you get bigger than 360. Here's a sample: http://gutfullofbeer.net/rotate.html
[edit] I've updated the code to deal with counter-clockwise rotation. Here's what my jQuery-based Javascript looks like:
$(function() {
var rotator = function(class, inc) {
var degrees = 0;
return function() {
$('.' + class)
.css({'-webkit-transform': 'rotate(' + degrees + 'deg)'})
.css({'-moz-transform': 'rotate(' + degrees + 'deg)'})
;
degrees += inc;
if (degrees > 360) degrees -= 360;
if (degrees < 0) degrees += 360;
};
};
setInterval(rotator('clockwise', 2), 33);
setInterval(rotator('counter-clockwise', -2), 33);
});

i dont see any button to make comments...sorry but i have to write here...
this is my code
deg is the value that the iphone give to me ant it can 0 to 360.
document.getElementById('mycompass').style.webkitTransform = "rotateZ("+-deg+"deg)";
when 'mycompass' goes to 359 and then 0, it rotates all way back...
in you example i can have values bigger than 360 so it doesn't work for me...
where is my error?

Related

Rotate function does not update location

I have a somewhat simple script I'm trying to get working where an image will rotate to point at your cursor, and then move the image to follow the mouse once the cursor has stopped. The problem I'm having is that the rotate function which I have borrowed from another solution will not update the location of the image. Once the image moves across the screen, it still compares the original location of the image to the mouse location when calculating the rotation.
Here's my rotate function:
function rotate(){
var offset = img.offset();
function mouse(evt) {
var center_x = (offset.left);//(img.width() / 2);
var center_y = (offset.top);//(img.height() / 2);
var mouse_x = evt.pageX;
var mouse_y = evt.pageY;
var radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
var degree = (radians * (180 / Math.PI) * -1) + -90;
img.css('-moz-transform', 'rotate(' + degree + 'deg)');
img.css('-moz-transform-origin', '0% 11%');
img.css('-webkit-transform', 'rotate(' + degree + 'deg)');
img.css('-webkit-transform-origin', '0% 13%');
img.css('-o-transform', 'rotate(' + degree + 'deg)');
img.css('-o-transform-origin', '0% 11%');
img.css('-ms-transform', 'rotate(' + degree + 'deg)');
}
$(document).mousemove(mouse);
}
I also have the entire project on jsfiddle here: http://jsfiddle.net/joshbox/t8kfyxec/3/
The problem is that you are calculating the angle of your image based on the position it has on load of the page, and not the current position (that may be different after it has moved).
This should be easily fixed by fetching the actual position each time you calculate the rotation angle. All you need to do is move var offset = img.offset(); inside your mouse() function and it should work fine.
While you are at it, I noticed you are setting the transform origin of your image along with the angle, while the origin actually remains fixed. May I suggest you move that to your css in stead.
I went ahead and updated your fiddle with my suggestion, along with some minor readability updates: http://jsfiddle.net/t8kfyxec/6/
A few notes on the changes I made:
It should be clearTimeout, not clearInterval
always include the w3c standard versions (as last option), not just the ones with the browser prefixes.
I removed the inner move function, as I could not see a good reason for it.
All events are now bound inside a single event handler, and the functions that get triggered only receive the data they require, not the entire event (which makes it easier to test, debug and reuse your code)
I hope this all makes sense. Feel free to ask if you want me to explain further.

Rotating on multiple axis with three.js

I'm experimenting with three.js and the deviceorientation event. I'm rotating a cube with my phone's accelerometer, using the technique used in the THREE.DeviceOrientationState plugin. Basically the coordinates from the deviceorientation event angles are converted to radians and applied to a meshes' rotation coordinates (mesh.rotation.x, etc). Pretty basic.
This technique works fine as long as the phone is not rotated more than 90 degrees, but once it hits 90 degrees the rotation get messed up and the cube basically "flips". Not sure how else to describe it. Also, rotation works perfectly when rotating on only one axis, but as soon as two or more axis are involved and the phone is rotated more than 90 degrees we have problems.
Basically I need to know how to combine more than one rotation angle so that the cube rotates with the phone no matter how much the phone is rotated. The cube should always mimic the rotation of the phone. (I know, it's a bit weird to do that since you can't see the phone screen when it has been rotated so much, but bear with me, I have a plan.)
I thought maybe I was hitting gimbal lock, so tried using quaternions, but I get the same results. To be honest, I'm a little embarrassed to be asking this question. It has been a while since I did any 3D programming, but I feel like I should know how to do this.
Here's some example code. What else do I need to do to combine the rotation angles?
$(window).on('deviceorientation', function(e) {
var x = (!e.originalEvent.beta ? 0 : e.originalEvent.beta) * Math.PI / 180;
var y = (!e.originalEvent.gamma ? 0 : e.originalEvent.gamma) * Math.PI / 180;
var z = (!e.originalEvent.alpha ? 0 : (e.originalEvent.alpha - 180)) * Math.PI / 180;
cube.rotation.x = x;
cube.rotation.y = y;
cube.rotation.z = z;
});
UPDATE: Here is a jsfiddle that illustrates the issue. It has been modified to use the DeviceOrientationControls feature, which is now included in three.js. You'll see that the problem persists. Basically I need the cube to mimic the orientation of the phone exactly, no matter how much I turn the phone.
UPDATE 2: I changed the shape in the above jsfiddle from a cube to a 3d rectangle that is in the shape of a phone. I think it's easier to see what's happening with a rectangle than a cube.

CSS3 and JS: Rotation on click per 90° to a full circle?

probably a simple question but … I don't know.
|​
I toggle a class active when clicking on a link that rotates #line by 90°. If the class is removed again the #line rotates back to 0°.
I wonder how I can make it rotate within a full circle?
So I click it the first time it rotates to the right to 90°. If I click it again It rotates back (to the left) to 0°. However I want it to rotate to to the right again till it reaches 180°. And so forth. So it should rotate within a full circle.
http://jsfiddle.net/5mCTd/2/
Simple, see it here.
var angle = 90;
$('#line').click(function() {
$(this).css ({
'-webkit-transform': 'rotate(' + angle + 'deg)',
'-moz-transform': 'rotate(' + angle + 'deg)',
'-o-transform': 'rotate(' + angle + 'deg)',
'-ms-transform': 'rotate(' + angle + 'deg)'
});
angle+=90;
});​
I've commented out the CSS & JS that is not required.
Do let me know if that's not the effect you're looking for.
Hope it helps. :)
Easiest way: do it with JavaScript. Increase the rotation amount by 90 each time and set the element's inline style to the new rotation amount.
The biggest problem is that theoretically you want it to go from 270 to 360 not 0. So just setting four classes (0, 90, 180, 270, ... uh ... 0) isn't going to help.
The tricky part here (thanks to Anthony Mills for pointing it out) is the transition from 270 to 360 -- you cannot simply return to 0, you have to go to 360, 450, etc. Therefore, we need to take the current angle and always add 90 to it.
Trying to get the current angle from the transform property and add 90 to it isn't worth it for this, since the transform is stored as a matrix by the browser and you'd have to reverse engineer the angle from the matrix (not very difficult and there are scripts out there to do it, but why bother?)
Instead, I'm going to use N, E, W, S (North, East, West, and South) to indicate state, just so you know where the letters are coming from. Default will be N, so...
|
And then a bit of jQuery. I use the class to figure out the current angle, so I don't have to reverse the matrix from transform.
$('#line').click(function() {
var rot = 360 * $(this).attr('data-turns');
switch ($(this).attr('class'))
{
case 'N':
rot += 0;
$(this).removeClass('N').addClass('E');
break;
case 'E':
rot += 90;
$(this).removeClass('E').addClass('S');
break;
case 'S':
rot += 180;
$(this).removeClass('S').addClass('W');
break;
case 'W':
rot += 360;
$(this).attr('data-turns', $(this).attr('data-turns') + 1);
$(this).removeClass('W').addClass('N');
break;
default:
$(this).attr('class', 'N');
}
$(this).css('transform', 'rotate('+rot+'deg)');
});
Note that this only works if you aren't using any classes on #line. If you are, you'll have to use if/else if/else with hasClass() instead of switch with attr('class'). Really, there are probably better fields to use than class for storing this information (the HTML5 data- fields, for example); I just used class because it was quicker/easier to write the CSS and jQuery.
EDIT: Added N2 for the 360deg option so rotation completes. Not 100% sure if the N2 to E transition will rotate as desired. Thanks to Anthony Mills for mentioning this.
EDIT2: Removed CSS styling altogether, switched to calculating the correct rotation based on the class and a particular data- field, namely data-turns.

rotate an image multiple times on click with jquery

I am trying to rotate an image using jquery that will rotate on multiple mouse clicks. Using the jquery-rotate plugin, the following code only rotates the image once (transforming to a canvas in firefox) and will no longer rotate on further clicks.
$(".drag-and-rotatable img").click(function() {
$(this).rotate(45);
});
I'm open to using other JavaScript libraries.
When you say rotate(45), you are rotating the image 45 degrees? (make sure it's not radians, I don't use the plugin) from the original rotation, so if you want to keep rotating you have to keep adding or subtracting degrees:
$(function() { // doc ready
var rotation = 0; // variable to do rotation with
$(".drag-and-rotatable img").click(function() {
rotation = (rotation + 45) % 360; // the mod 360 probably isn't needed
$(this).rotate(rotation);
});
});

Rotate sprite javascript

I have a sprite animation, a small cannon rendered using a 3D app. I have exactly 360 frames for a 360 degree turn. Each image has a 100x100 pixel size.
So basically what I am trying todo is when I click anywhere in the page, the barrel of the cannon needs to rotate to point at the mouse cursor, sound simple maybe but I can't really get it to work very well, perhaps cause my math skills is lacking :P
What I currently have is something like this
/* This is my div with the cannon background image (360 images stitched into one) each "image area" is 100x100px */
obj.cannon = $('#cannon');
/* Get the X/Y of the cannon loc in the dom */
var cannonX = $(obj.cannon).offset().left;
var cannonY = $(obj.cannon).offset().top;
/* Get radians using atan2 */
var radians = Math.atan2(e.pageY-cannonY, e.pageX-cannonX);
/* Convert to degrees */
var degrees = radians * (180/Math.PI);
And this is where I am, I mean since the image width is 100px and I need to move the background-position by 100px to move the cannon one degree right, because 360 images * 100px = 36000px in total. So the stitched sprite is like 36000px wide.
So
Insert weird calculation here based on the current backgroundPosition of the image-sprite and apply new backgroundPosition based on where you click with the mouse cursor, then use some sort of setTimeout(animateIt, speed); to "animate" the background position to the new position.
function animateIt(){
if(newpos!=targetpos) { //Use < > here if we need to add or remove
newpos+=100; //Until we arrive at the new backgroundPosition
$(obj.cannon).css({backgroundPosition: newpos+'px' });
setTimeout(animateIt, speed);
}
}
Am I at all on the right track here, am I thinking correctly about this? I feel stupid, this should be a simple thing but right now I am having a brain meltdown I think =P. My problem is I don't know how to properly arrive at the "new target backgroundposition" and then animate it ++ or -- based on the current background position :/
Well, here is a simplified working example with 10 images.
I'll post the code and jsFiddle now, and I might come back later to cover it in depth. But basically you just order your images correctly, and then you pick the segment by using (Segments - Math.floor(degree / (360 / segments))). You may have to adjust your 0 degree. For example, I made my 0 equal to what would normal by 90.
Pay attention to the fact that the screen coordinates, x and y, increase right and down. This makes the degrees of atan work clockwise instead of the usual counter clockwise in coordinate systems where x and y increase right and up.
I added in some text output that shows the degrees and image segment being shown.
jQuery handles normalizing the x and y position nicely. Just take care that your CSS setup is cross browser.
Working jsFiddle example
Here's our image:
Here's our HTML:
<div id="main"><div id="img"></div></div>
<div id="info">
<span></span><br/>
<span></span>
</div>
​
CSS:
div#main {
width:500px;
height:500px;
border:2px #000 solid; }
div#img {
width:94px;
height:119px;
overflow:hidden;
top:50%;
left:50%;
margin-left:-45px;
margin-top:-60px;
position:relative;
background-image:url('http://imgur.com/3UPki.png');
background-position:0;}
div#info {
position: absolute;
bottom:0;
left:0; }
​
Javascript / jQuery:
$(function() {
var position = $("div#img").position(),
mouseX, mouseY, imgX, imgY, degree;
imgX = position.left;
imgY = position.top;
$("#main").mousemove(function(e) {
// degree is arctan y over x (soh,cah,toa)
degree = Math.atan2((e.pageY - imgY),(e.pageX - imgX))*(180 / Math.PI);
degree = (degree - 90) % 360;
// jQuery normalizes pageX and pageY
// transfrom from -180 to 180 ==> 0 to 360
if (degree < 0) degree = 180 + (180 - (Math.abs(degree)));
rotate(degree);
$("span:first").html("Segment: " + (9 - Math.floor(degree / 36)));
$("span:last").html("Degree: " + Math.floor(degree));
});
function rotate(degree) {
var off = 9 - Math.floor(degree / 36);
$("div#img").css("background-position",-off*94);
}
}); ​
Working jsFiddle example
Keep in mind that the degrees you get from atan will start pointing right for zero degrees and go clockwise from there (-90 is up, 90 is down).
Each position of your image should correspond to a specific angle. Once you have the degrees measured (it looks like you have that part right), use some type of mapping to translate your degrees to the proper image offset. I don't know what your image looks like so I can't help with that. Assuming your image starts pointing right, and goes around clockwise from there, the degrees will correspond directly the the offset for the right image. (I suggest you arrange your frames like this for ease...)

Categories

Resources