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.
Related
When I rotate an element via
myElem.css('-moz-transform', 'rotate(' + degree + 'deg)');
myElem.css('-webkit-transform', 'rotate(' + degree + 'deg)');
myElem.css('-o-transform', 'rotate(' + degree + 'deg)');
myElem.css('-ms-transform', 'rotate(' + degree + 'deg)');
I need to get its correct left/top position...
myElem.getBoundingClientRect().left
...does not deliver the correct x/y positions. As well as f.e. using jQuery...
$(myElem).position().left
...delivers wrong values.
Here´s an image to show what I mean
(source: dasprinzip.com)
What to do?
I wrote a JSFiddle for you which hopefully will give you the ingredients to work everything out:
http://jsfiddle.net/pM54e/2/
The span in the Fiddle is to test if the value is correct. Give it the height that's calculated by the formula and see if it matches with the corner of the orange box.
var numY = Math.abs(myElem.position().top + sign*( (myElem.outerHeight() / 2) - Math.sin(degree)));
This will determine the Y-position of the rotated elements top left corner.
Keep in mind that (myElem.outerHeight() / 2) is the transform-origin of your element. Default is 50% of the elements height and width. If you change the origin, you have to change that value in the formula.
I don't have much time right now to figure out the x-position and provide a more detailed example but i'll update my answer with more tests, details and the x-value when i do find time tomorrow.
Hope this helps
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.
I'm trying to make the so called fine tune thing. Basically this looks like: http://jsfiddle.net/r9KQK/1/
Later I'll have some audio player and this thing will help to select seconds when we use it on a tablet.
The problem is when you try to move the red circle it strangely drops when it passes top and bottom of the green circle, but not at 0 or PI/2, something like at -260..-269 and 181..190 degrees
Just try to move it and you'll see the bug.
What's wrong in my code?
Thanks in advance
update
Last update: http://jsfiddle.net/r9KQK/17/
In this example I get degrees in 0..360 range. But instead I should get delta degrees between the point where I start dragging and where I end it, but I can't work out the maths. I should also take into account the direction of red circle, so that delta will be + or - =\
update
Finally: http://jsfiddle.net/r9KQK/18/
But the code is really awful. Though it's 2:46 AM and I'm kind of sleepy, so...
But anyway I think it could be much more simplified
That's happening because your parameter to Math.atan goes to infinity when DeltaX is zero. I recommend using atan2, which automatically handles this corner case:
function(dx, dy, x, y)
{
var deltaY = this.oy + dy - fineTuning.ring.attr('cy');
var deltaX = this.ox + dx - fineTuning.ring.attr('cx');
var angle = Math.atan2( deltaY, deltaX );
// etcetera, etcetera
Or check the fiddle.
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...)
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?