javascriptrotationtransformdragmousedown

Rotate element with mouse drag


I'm working on a map feature which should allow the user to drag rotate the map with the mouse.

This is the code of my mousedown event:

rotate.addEventListener('mousemove', (e) => {
    const elem = e.target;
    if (mouseDown) {
    const boundingRect = elem.getBoundingClientRect();
    const center_x = boundingRect.left + boundingRect.width / 2;
    const center_y = boundingRect.top + boundingRect.height / 2;
    const mouse_x = e.pageX;
    const mouse_y = e.pageY;
    const radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
    const degree = radians * (180 / Math.PI) * -1 + 90;
    rotate.style.transform = `rotate(${degree}deg)`;
  }
});

And here is the fiddle of the example: https://jsfiddle.net/8uexv2cp/3/.

So as you can see, it's kind of working. The problem is that if you start dragging in the top left corner, the element spins around (after this has happened it seems to be working as it should). Ideally I'd want it to just start rotating from the point that i select with the mouse and not having it flick around at the start.

What am I missing here?

I'd be happy to hear your thoughts about this.

Thanks


Solution

  • So here is my solution after some fiddling.

    on dragging start I save to original rotation angle of the element. Check this link for how to parse it out. And I also store the original point, from which we are dragging. This will be useful to calculate the direction of the dragging, once we start moving.

    let currentDegree: number = 0;
    let center: {x: number, y: number}; 
    
    function rotationStart(event: MouseEvent) {
       currentDegree = getCurrentRotation(element);
       center = {x: event.pageX, y: event.pageY}
    }
    

    Now it is time to the magic for moving. I calculate the direction of the movement and store it in radians. This is handy since, the positive value means I have to increase the degrees (rotate clockwise) and negative value is for decreasing (rotating anticlockwise). I just simply apply the new degrees to the element and store the new center element to recalculate the vector next time.

    function rotation(event: MouseEvent) {
       let radians = Math.atan2(event.pageX - center.x, event.pageY - center.y);
    
      if (radians > 0) {
        ++currentDegree;
      } else {
        --currentDegree;
      } 
    
      el.style.transform = `rotate(${currentDegree}deg)`;
      center = {x: pageX, y: pageY};
    }
    

    You can possibly control the speed of the rotation by not just doing ++currentDegree, but doing something like currentDegree = currentDegree+3

    Seems to be working pretty well.