Search code examples
javascriptmathtrigonometryradians

Angle to cursor, with different orientation of zero degrees


I'm trying to make a utility function to determine the angle from an element to the mouse cursor, but allow our devs to change where 0 is located. So this needs to return the angle as degrees between 0deg through to 360degs.

So I'm trying to write a function that can do that. I have it working for zero at due west, but need to get it working for zero at north, east and south.

It' surprisingly difficult. I have tried just adding 90deg to the radians with 90° × π/180 = 1.571rad but then struggled to get it back to degrees for the return.

function getAngleToCursor(el, evt, orientation = "north") {
    const mouseX = evt.clientX
    const mouseY = evt.clientY
    
    const elRect = el.getBoundingClientRect()
    const elX = elRect.left + elRect.width / 2
    const elY = elRect.top + elRect.height / 2

    const rads = Math.atan2(mouseY-elY, mouseX-elX)
    let angle = 0

    // Change where zero is located
    switch(orientation) {
        case "west":
            angle = rads * 180 / Math.PI + 180
            break;

        case "north":
            angle = ?
            break;  

        case "east":
            angle = ?
            break;  
        
        case "south":            
            angle = ?
            break;
    }

    return angle
}

Codepen here


Solution

  • Skip to the ending: Final version of it is here for future reference: https://github.com/funkhaus/fuxt/blob/master/utils/angleDistanceToCursor.js

    Okay, I think I get what you mean. I’ve posted the updated code below:

    function getAngleToCursor(el, evt, orientation = "north") {
        // get normal angle from mouse to element
        const mouseX = evt.clientX
        const mouseY = evt.clientY
        const elRect = el.getBoundingClientRect()
        const elX = elRect.left + elRect.width / 2
        const elY = elRect.top + elRect.height / 2
    
        const rads = Math.atan2(mouseY-elY, mouseX-elX)
        let angle = rads
        const whole = Math.PI * 2
    
        // Change where zero is located
        switch(orientation) {
            case "west":
                angle -= whole / 4
                break
    
            case "north":
                angle += 0
                break
    
            case "east":
                angle += whole / 4
                break
            
            case "south":            
                angle += whole / 2
                break
        }
    
        // convert angle to range between 0 and 360 (although we’re working in radians, of course)
        angle = ((angle % whole) + whole) % whole
    
        // returns angle in degrees
        return angle * 180 / Math.PI
    }
    

    I haven’t tested this by the way, but the concept is pretty sound even if the code isn’t perfect.