Parsley & Sprouts recently redesigned the website for the Abide Idea Company, an idea generation/strategic development consultant. The headline for the site reads, “A trusted guide on your journey to develop new ideas,” and we pushed this metaphor with a graphic of a compass that stays fixed on the page as you scroll down.
One interactive element that I was really happy with was making the needle of the compass spin so that it’s always pointing at your cursor as you browse the site. This was done with a combination of jQuery and CSS3 transforms.

HTML
<section id="masthead" class="full-width" role="masthead">
<div class="full-content">
<div class="content">
<h2>A trusted guide on your journey to develop new ideas.</h2>
<div class="compass_container">
<div id="compass">
<img class="compass" src="compass.png">
<img class="needle" data-rotate="" src="needle.png">
<img class="circle" src="circle.png">
</div>
</div>
<h3>A friend to ideas and the people who have them.</h3>
</div>
</div>
</section>
The compass consists of three separate image files — one for the compass itself, one for the needle, and one small circle at the center of the needle. All are located in the masthead, so that the compass can be positioned relative when you’re at the top of the screen. Then, once you scroll down far enough that it hits the center of the screen, the JavaScript adds a class of ‘fixed,’ which does what you’d expect — fixes it in the center. The part here that helps us rotate is the (currently empty) data-rotate attribute on the needle. I had originally thought to use classes (i.e. class=”rotate-*” where * is some degree value), but that makes it very difficult to replace without some regex, which can be tricky. Data is more succinct, and can still be styled with CSS, so it won out. Depending on the cursor location, data-rotate is assigned via jQuery:
JS
var mouseX, mouseY, needleX, needleY, angle;
var needle = $('.needle');
$(document).mousemove(function(e){
mouseX = e.pageX;
mouseY = e.pageY - $(window).scrollTop();
needleX = needle.offset().left + needle.width()/2;
needleY = needle.offset().top - $(window).scrollTop() + needle.height()/2;
if (mouseX > needleX) {
angle = Math.atan((mouseY - needleY)/(mouseX - needleX));
angle = 180 * angle/Math.PI + 90;
} else {
angle = Math.atan((mouseY - needleY)/(mouseX - needleX));
angle = 180 * angle/Math.PI + 270;
}
if (Math.abs(mouseX - needleX) < 80 && Math.abs(mouseY - needleY) < 80) {
angle = $('.needle').attr('data-rotate');
}
angle = 3 * Math.round(angle/3);
needle.attr('data-rotate', angle);
});
First I set some variables — the x and y positions of both the cursor and the needle, and the angle between them. The ‘needle’ variable is, of course, the needle. Whenever the mouse is moved, these variables are recalculated. For the y position of both the cursor and the needle, the amount the window has scrolled from the absolute top of the page also needs to be recalculated — otherwise the jQuery .offset() method gives us the position relative to the page, not the window. For the needle, both the x and y values have got 1/2 of the width and height (respectively) of the image added, so that the position is calculated from the center of it, not the top left corner.
Once we’ve got those four variables, we can calculate the angle formed between the center of the needle and the cursor. Thinking back to high school trigonometry Googling brought me to the arctangent function. Imagine a right triangle drawn with two points at the center of the needle and the cursor, and the third formed where orthogonal lines from those points intersect:

Subtracting the needle’s y position from the mouse’s y position (and needle x from mouse x) gives us the lengths of the triangle’s two legs, which is what we need for our arctan function. One thing to note is that the output of arctan changes depending on if the horizontal component is positive or negative (if the cursor is to the left or the right of the needle). Obviously we don’t want this to happen, so I separated the angle output into two different cases to compensate for this. Then we calculate the angle in each by taking the arctangent, converting to degrees (arctan outputs radians by default — degrees are just easier on the eyes), and adding either 90 or 270, depending on if we’re to the right or left. This value comes from the fact that 0 degrees is actually straight out to the right, and we want the needle’s resting state to be straight up. Finally, we round the angle to the nearest 3 degrees (more on that in a bit…), and set the data-rotate attribute of the needle equal to the angle.
The last if statement checks to see if the user is hovering over the needle. Without it in place, as you get closer to the needle, the needle will start acting really volatile, spinning all over the place wildly depending on where exactly the cursor is. This statement locks it in its current place (where it was when the cursor entered its space) until the cursor moves out again.
And all of this is re-done every time you move the mouse.
Of course, on its own, this JavaScript does nothing visually. This is where the CSS transforms come in. In the CSS file, using the [data-] selector, I wrote declarations for each angle we can have. Writing out the full 360 degrees seemed a little excessive. It turns out that the eye can hardly tell the difference between rotating it to every degree vs. the nearest multiple of three (i.e. rotating to 151 degrees looks almost identical to 150). This has the advantage of cutting down the markup by 2/3.
I still wasn’t about to write 120 separate declarations for each possibility, with vendor prefixes to boot:
CSS
[data-rotate="3"] {
-webkit-transform: rotate(3deg);
-moz-transform:rotate(3deg);
-ms-transform:rotate(3deg);
-o-transform:rotate(3deg);
transform:rotate(3deg);
}
[data-rotate="6"] {
-webkit-transform: rotate(6deg);
-moz-transform:rotate(6deg);
-ms-transform:rotate(6deg);
-o-transform:rotate(6deg);
transform:rotate(6deg);
}
etc. up to [data-rotate="357"]
Even copying and pasting, that would be a huge drag, and not easily maintainable. Fortunately, using Compass/SASS, it can be done in a simple @for loop:
SASS
@for $i from 0 through 120 {
[data-rotate="#{3*$i}"] {
@include transform(rotate(#{3*$i}deg));
}
}
This outputs all of the above CSS, but makes it easy to change or adjust in the future.
Transforms actually have pretty decent browser support, which the exception of IE8 and below. This is really a prime example of progressive enhancement — users on older version of Internet Explorer will see a fixed compass and needle (pointing straight up), which still furthers the “guide” metaphor without the interactivity. As an added bonus, although touch screens don’t have cursors, they do call .mousemove() whenever the user taps on the screen, so the needle will change its position to point where you tap. Most probably won’t notice it, but it’s a nice feature for those who do.
So, put this all together, and you’ve got yourself one heck of a nice spinning needle. Don’t get too dizzy.