Setting the Origin

In many use-cases, an element will rotate around its center point. In CSS, the center of rotation for an element is defined by it’s transform-origin property, which has a default value of center center. However, it’s important to note that the mathematical center of the element - as represented by the default value - may not necessarily be the visual center of an element. This is especially true if the element is asymmetric: for example, a circle with a pointer, as shown in Figure 1.
Figure 1: Mathematical center (left) vs. visual center (right). CSS will always use the mathematical center as the transform-origin value by default

So, before rotating an element dynamically with JavaScript, it’s important to set the transform-origin correctly, followed by testing it by using different values for transform: rotate(). For the element shown in Figure 1, the CSS is:

touch-action: none; is used to prevent the default touch behaviour in IE on devices with touch support. Note that the transform-origin can also be set with percentages for responsive designs, as shown in the associated CodePen demo.

We need to find the centering information in JavaScript, since our script will also need to know the transform-origin of the element in order to do the calculations that follow. Added to the bottom of the page:

This provides is with the center of the element relative to the page, rather than relative to to the element’s own top left corner. pageXOffset and pageYOffset are subtracted in case the page has been scrolled.

If the mathematical and visual center of the element were the same, you could use the following:

And then call this function from event listeners. Note that normally the touch area would be limited to the element around the pointer; otherwise, the user may have a very hard time scrolling the page with gestures.

Holding Steady

If you are building on a complex page, you may want to wait on the page to load before measuring the position of elements, since trying to take measurements before the page has fully loaded may yeild incorrect values. If so, the complete code would become something like: