'Rotate element based on touch event

I'm looking for some guidance and/or examples to create a way to touch an object and rotate it. Let's say we had an image of a circle, I want to be able to touch one point, drag around that circle and manipulate it to spin as my finger moves.

I'm finding plenty of tutorials on rotating:

I just need to get the touch part incorporated.

Anyone done this before or can shed some light?



Solution 1:[1]

You could simply map the rotation of the touch to the element's transform: rotate(Ndeg) CSS.

Not all touch-enabled browsers provide a rotate property on the event object, so you can figure out the value with a splash of math.

document
.getElementById("some-element")
.addEventListener("touchmove", function(event) {
    var rotation = event.rotation;

    // This isn't a fun browser!
    if ( ! rotation) {
         rotation = Math.atan2(event.touches[0].pageY - event.touches[1].pageY,
               event.touches[0].pageX - event.touches[1].pageX) * 180 / Math.PI;
    }

    // Take into account vendor prefixes, which I haven't done.
    this.style.transform = "rotate(" + rotation + "deg)":
});

You will also need to store the previous rotation value and adjust this one based off it if you want the user to be able to rotate it more than once. :)

Solution 2:[2]

Rotate element on mouse/touch drag

Here's a complete example on how to drag-rotate an Element:

Store the start angle, which is the relative angle at coordinates minus the current angle.
Rotate by the difference (delta) of the mousemove XY coordinates angle from center minus the start angle.

const rotate = (EL) => {
  
  let ang = 0; // All angles are expressed in radians
  let angStart = 0;
  let isStart = false;

  const angXY = (ev) => {
    const bcr = EL.getBoundingClientRect();
    const radius = bcr.width / 2;
    const { clientX, clientY } = ev.touches ? ev.touches[0] : ev;
    const y = clientY - bcr.top - radius;  // y from center
    const x = clientX - bcr.left - radius; // x from center
    return Math.atan2(y, x);
  };

  const mousedown = (ev) => {
    isStart = true;
    angStart = angXY(ev) - ang;
  };

  const mousemove = (ev) => {
    if (!isStart) return;
    ev.preventDefault();
    ang = angXY(ev) - angStart;
    EL.style.transform = `rotateZ(${ang}rad)`;
  };

  const mouseup = () => {
    isStart = false; 
  };

  EL.addEventListener("mousedown", mousedown);
  document.addEventListener("mousemove", mousemove);
  document.addEventListener("mouseup", mouseup);
};

document.querySelectorAll(".rotate").forEach(rotate);
.rotate {
  border-radius: 50%;
  width: 180px;
  cursor: ns-resize;
}
<img class="rotate" src="https://www.gravatar.com/avatar/1771ce957b38fbb7f9bb01f58b89fd3f?s=328&d=identicon&r=PG" alt="Wheel image">
<img class="rotate" src="https://i.stack.imgur.com/qCWYU.jpg?s=328&g=1" alt="Wheel image">

Solution 3:[3]

You will want to look at implementing MouseDown, MouseUp, and MouseMove event listeners on your canvas, since "touch" interaction with a touch screen is nothing more than a fancy mouse movement.

MouseDown and MouseUp are useful for finding if the user is dragging across the canvas or not. MouseMove is where you will need to put the rotation processing logic. The rest is implementation:

/* Borrowed from http://www.marceloduende.com/blog/?p=25 */
function addCrossBrowseEventListener(myElement, myEvent, myFunction) {  
    if(myElement.addEventListener){  
        myElement.addEventListener(myEvent, myFunction, false);  
        return true;  
    } else {  
        myElement.attachEvent('on'+myEvent, myFunction);  
        return true;  
}

Where myElement is the DOM element for your canvas, myEvent is the name of the event to add a listener for, and myFunction is the name of the function that is tasked with the logic for the event.

Alternatively, you could use jQuery to do most of this, as it has a number of cleaner methods for event handling.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nicu Surdu
Solution 2
Solution 3 Justin Pearce