Consider a page that has an element that you want to follow a compass bearing:

We can use the css rotation transformation to change its orientation:

Set the rotation centre
Depending upon the browser (or other styling), the element may not rotate around the center of the image. For example, this one rotates around the top-left corner:

transform:rotate(15deg);
We suggest that you always set the rotation center to the center of the image with transform-origin:

transform:rotate(15deg);transform-origin: 50% 50%;
Those buttons
The buttons above allow you to change the rotation of the element by +/- 1°
We use a simple jQuery function to do that, which reads the current rotation of the element, calculates the new rotation and finally sets that.
This is the routine:
function rotate(id,by) { var r = parseInt($('#'+id).attr("data-rotation")); r+=by; $('#'+id).attr("data-rotation",r); $('#'+id).css("transform","rotate("+r+"deg)"); $('#'+id+'_caption').html(r); }
For convenience, we are using a bespoke attribute on the element, "data-rotation", to store the current rotation value. This means we don't have to parse out the transform style.
Set a viewport around it
When the image is being rotated, the amount of screen space used for it changes.
To keep a fixed space, put the image into a viewport and hide any overflow:
<img src="/assets/img/rotation_compass.png" height="237" width="237" alt="compass" style="background-color:#ff0000;height:237px;width:237px;margin-left:auto;margin-right:auto;display:block;transform-origin: 50% 50%;transform:rotate(15deg);"/>
</div>
15deg
Overcome the corner problem
You will notice the white background showing in the corner as the image rotates which isn't what you want on your page.
The solution is to make the element being rotated larger. It should have a height and width at least as big as the diagonal of the viewport.
You can either increase the size of the image itself, or nest it inside another div that becomes the target for the rotation.
Using pythagoras' theorem, the length of the viewport diagonal is easily calculated:
diagonal = sqrt((viewportWidth * viewportWidth) + (viewportHeight * viewportHeight))
In our example, the viewport width and height are both 237px
Therefore the diagonal length is sqrt((237*237)+(237*237)) = 335.168, which we will round up to 337, to make (later) calculations easier:
<div style="width:337px;height:337px;display:block;background-color:#ff0000;transform-origin: 50% 50%;transform:rotate(15deg);">
<img src="/assets/img/rotation_compass.png" height="237" width="237" alt="compass" style="margin-left:50px;margin-top:50px;" />
</div>
</div>
15deg
Since the size of the rotating element is 337x337px but the viewport is 237x237px, we need to shift the image left and up by half the difference (50px) to centre it again:
<div style="margin-left:-50px;margin-top:-50px;width:337px;height:337px;display:block;background-color:#ff0000;transform-origin: 50% 50%;transform:rotate(15deg);">
<img src="/assets/img/rotation_compass.png" height="237" width="237" alt="compass" style="margin-left:50px;margin-top:50px;" />
</div>
</div>
15deg
Now we have the image rotating properly, we can relate it to a compass heading/bearing
If we are heading North East, we want NE on the image to be pointing up | If we are heading South East, we want SE on the image to be pointing up | If we are heading South West, we want SW on the image to be pointing up | If we are heading North West, we want NW on the image to be pointing up |
![]() |
![]() |
![]() |
![]() |
NE bearing = 45° | SE bearing = 135° | SW bearing = 225° | NW bearing = 315° |
NE rotation = 315° | SE rotation = 225° | SW rotation = 135° | NW rotation = 45° |
In general
rotation = 360 - bearing
And that a bearing lies between 0° and 360°
Bearing 0deg
And here is the updated jQuery function that deals with converting from bearing to rotation:
function bearing(id,by) { var b = parseInt($('#'+id).attr("data-bearing")); b+=by; if(b<0) b+=360; if(b>360) b-=360; var r = 360 - b; $('#'+id).attr("data-bearing",b); $('#'+id).css("transform","rotate("+r+"deg)"); $('#'+id+'_caption').html(b); }
Again, we use the bespoke attribute "data-bearing" to hold the current bearing value for the element.
Smoothing it out
You will have noticed that changing the bearing causes a jumpy change as the image is redrawn, which doesn't make a very nice UI to look at.
Fortunately we can use a CSS transition to animate the change in rotation:
<div style="margin-left:-50px;margin-top:-50px;width:337px;height:337px;display:block;background-color:#ff0000;transform-origin: 50% 50%;transform:rotate(0deg);transition: 1s linear;">
<img src="/images/rotation_compass.png" height="237" width="237" alt="compass" style="margin-left:50px;margin-top:50px;" />
</div>
</div>
Bearing 45deg
Fixing the spin
If you change the bearing so that it crosses the 0/360 line, you will see that the transition now causes the image to spin.
This is due to the linear nature of the transition. Changing from a rotation of 359° to 1° is not a 2° anti-clockwise change (what we want to see) but a 358° change.
Fortunately, while a bearing will only ever be between 0° and 360°, rotation can go beyond 360.
That is to say, a rotation of 370° is equivalent to a rotation of 10°.
Bearing 45deg
The final jQuery function
function bearingNoSpin(id,by) { var oldH = parseInt($('#'+id).attr("data-heading")); var oldB = parseInt($('#'+id).attr("data-bearing")); var newB = oldB + by; if(newB<0) newB+=360; if(newB>360) newB-=360; var delta = oldB - newB; if(delta>180) delta = delta - 360; if(delta<-180) delta = 360 + delta; var newH = oldH +delta; $('#'+id).attr("data-bearing",newB); $('#'+id).attr("data-heading",newH); $('#'+id).css("transform","rotate("+newH+"deg)"); $('#'+id+'_caption').html(newB); }