Search code examples
javascriptmathtrigonometrypi

Why does my menu jump the second time I try and drag it to a new item?


I've made a rotating menu.

To select an item you rotate the menu by clicking and dragging.

http://codepen.io/PaulBunker/pen/ZGgxvY

var dragging = false;
var links = $('.menu a');
var radius = 520;
var degree = 0.262;
var angle = 0.79;
var orgX;
var orgY;
var offset = $('.menu').offset();
var newangle;
var origradians;

function setItems(angle) {
    var internalangle = angle;
    links.each(function() {

        var y = Math.round(radius * Math.cos(internalangle));
        var x = Math.round(radius * Math.sin(internalangle));

        $(this).css({
            left: x + 'px',
            top: 0-y + 'px',
            display:'block',
        });

        $(this).addClass(y);


        if (y < 10 & x > 0) {
            $(this).addClass('active');
        }
        if ( y < -10 || y > 0) {
            $(this).removeClass('active');
        }


        internalangle += degree;

    });
}


$(function() {

    $(document).mousedown(function(evt) {
        orgX = evt.pageX - offset.left;
        orgY = evt.pageY - offset.top;
        orgradians = Math.atan2(orgX, orgY);
        dragging = true;
    });

    $(document).mouseup(function() {
        dragging = false;
        angle = newangle;
    });

    $(document).mousemove(function(evt) {
        if (dragging) {

            var x = evt.pageX - offset.left;
            var y = evt.pageY - offset.top;
            var radians = Math.atan2(y, x);


            newangle =  (orgradians + radians) - angle;
            console.log (orgradians , radians, angle, newangle);

            setItems(newangle);
            console.log(newangle);
        }
    });

    setItems(angle);

});

My problem is after the first drag to select an item.

At the beginning of the second drag the menu jumps into the wrong position.

on 'mouseup' I save the 'newangle' variable and override the 'angle variable'.

I suspect there is an error somewhere in the line

newangle =  (orgradians + radians) - angle;

I've been tearing my hair out to try and get this to work!

Thanks in advance for any guidance!

-Paul


Solution

  • If you don't use trigonometry on mouse move and just use it once when the radius of the circle is defined it'll fix the issue:

    1. Work out the rotation speed based on the radius of the circle:

      var rotationSpeed = Math.atan(1/radius);
      
    2. Use the rotation speed and the difference in the Y position of the cursor on mouse move:

      var newangle = lastangle - (difY*rotationSpeed);
      

    http://codepen.io/stevenarcher/pen/yNmRyP

    var dragging = false;
    var links = $('.menu a');
    var radius = 520;
    var degree = 0.262;
    var lastY;
    var offset = $('.menu').offset();
    var lastangle = 0;
    var rotationSpeed = Math.atan(1/radius);
    
    
    function setItems(internalangle) {
        links.each(function() {
    
            var y = Math.round(radius * Math.cos(internalangle));
            var x = Math.round(radius * Math.sin(internalangle));
    
            $(this).css({
                left: x + 'px',
                top: 0 - y + 'px',
                display:'block',
            });
    
            $(this).addClass(y);
    
            if (y < 10) {
                $(this).addClass('active');
            }
            if ( y < -10 || y > 0) {
                $(this).removeClass('active');
            }
            internalangle += degree;
    
        });
    }
    
    
    
    $(document).mousedown(function(evt) {
        lastY = evt.pageY - offset.top;
        dragging = true;
    });
    
    $(document).mouseup(function(evt) {
        dragging = false;
    });
    
    $(document).mousemove(function(evt) {
    
        if (dragging) {    
            var y = evt.pageY - offset.top;
            var difY = lastY - y;
            lastY = y;
            var newangle = lastangle - (difY*rotationSpeed);
            setItems(newangle);
            lastangle = newangle;
        }
    });
    
    setItems(0);