How to make an animate up to the mouse position and after starting to follow the mouse?

0

I have the following code

$(document).ready(function(){
    var LeftInicial = $('.MarcadorMenu').css('left');
    var WidthInicial = $('.MarcadorMenu').css('width');
    $('.CorpoMenuHead').hover(function(){
        $('.CorpoMenuHead').bind('mousemove', function(e){
            var Tam1 = $('.MenuHead').innerWidth();
            var Tam2 = $('.CorpoMenuHead').innerWidth();
            $('.MarcadorMenu').css({
                left: e.pageX -(Tam1-Tam2)/2-50,
                width: '100px'
            });
        });
    },function(){
        $('.MarcadorMenu').animate({
            left: LeftInicial,
            width: WidthInicial
        });
    });
});

With this when I put the cursor on the div CorpoMenuHead o the div MarcadorMenu "jumps" to that position to start following the mouse pointer

Would you like to make div reach the mouse location with a animate so as not to get such a rough and somewhat ugly effect?

Fiddle

I made a change and got closer than I wanted

Fiddle updated

I think the question to solve this bug now is "can you check if an event is running?" ex: will be called animate and while this animate will not have to be executed it will not start the command .css() in my else

    
asked by anonymous 12.05.2014 / 16:27

2 answers

2

This code moves smoothly from div to the cursor, every time the cursor enters the menu area, and the cursor is "away" from div .

Once div reaches the cursor, it follows it directly (at least while the cursor has not exited the menu).

When the cursor returns to the menu, smooth movement occurs if it is "far" from div , otherwise it just takes div to the cursor.

The concept of "far" and how smooth the div movement can be configured just by changing the indicated coefficients along the code.

The code below is summarized in this fiddle .

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="pt-br">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Mouse</title>
    <script type="text/javascript">
        //
        //Alerta! Não foi testado com IE8-, só com IE9+. Os outros browsers estão OK!
        //
        var ultX = 0, ultT, percentualConcluido = 0, meuDiv, meuMenu, meuMenuLeft,
            alcancouOCursor = true, primeiraVez = true;
        //prepara algumas funções necessárias para animação, temporização e localização
        if (!window.requestAnimationFrame) {
            window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) { return window.setTimeout(function () { return callback(+new Date()); }, 1000 / 60); });
        }
        if (!Date.now)
            Date.now = function () { return (+new Date()); };
        function elementLeft(element) {
            //https://github.com/carlosrafaelgn/GraphicalFilterEditor/blob/master/Common.js
            var left;
            if (element.getBoundingClientRect) {
                left = element.getBoundingClientRect();
                left = left.left + window.pageXOffset;
            } else {
                left = 0;
                while (element) {
                    left += element.offsetLeft;
                    element = element.offsetParent;
                }
            }
            return left;
        }

        function meuMenu_MouseEnter(e) {
            //armazena a coordenada X (relativa a meuMenu) para animar
            ultX = e.pageX - meuMenuLeft;
            //não deixa meuDiv passar do fim de meuMenu
            if (ultX > (meuMenu.clientWidth - 45))
                ultX = (meuMenu.clientWidth - 45);
            //
            //esse bloco if/else pode ser removido, caso desejado...
            //
            if (primeiraVez) {
                primeiraVez = false;
            } else {
                //determina se realmente é necessário iniciar uma animação,
                //conforme a distância entre o cursor e meuDiv
                var meuDivLeft = elementLeft(meuDiv) - meuMenuLeft;
                if (Math.abs(meuDivLeft - ultX) < 50)
                    return true;
            }
            //reinicia a animação
            alcancouOCursor = false;
            ultT = Date.now();
            requestAnimationFrame(animaDiv);
            return true;
        }

        function meuMenu_MouseMove(e) {
            //armazena a coordenada X (relativa a meuMenu) para animar
            ultX = e.pageX - meuMenuLeft;
            //não deixa meuDiv passar do fim de meuMenu
            if (ultX > (meuMenu.clientWidth - 45))
                ultX = (meuMenu.clientWidth - 45);
            if (alcancouOCursor) {
                //não há necessidade de animar, apenas define a posição
                meuDiv.style.left = ultX + "px";
            }
            return true;
        }

        //essa função vai animar a posição do div de uma forma não muito brusca
        function animaDiv() {
            //ver comentário dentro da função document_MouseMove
            if (alcancouOCursor)
                return;

            //Date.now() retorna um tempo em milissegundos, por isso divide por 1000
            var agora = Date.now(), deltaT = (agora - ultT) / 1000, meuDivLeft, velocidade;
            ultT = agora;
            requestAnimationFrame(animaDiv);

            //obtém a coordenada left de meuDiv, relativo a meuMenu
            meuDivLeft = elementLeft(meuDiv) - meuMenuLeft;

            //é possível ajustar a velocidade alterando os coeficientes aqui!!!
            velocidade = Math.abs(ultX - meuDivLeft) * 5;
            if (velocidade > 2000)
                velocidade = 2000;
            else if (velocidade < 100)
                velocidade = 100;

            //anima o div, conforme a velocidade
            if (meuDivLeft < ultX) {
                meuDivLeft += velocidade * deltaT;
                if (meuDivLeft >= ultX) {
                    alcancouOCursor = true;
                    meuDivLeft = ultX;
                }
            } else {
                meuDivLeft -= velocidade * deltaT;
                if (meuDivLeft <= ultX) {
                    alcancouOCursor = true;
                    meuDivLeft = ultX;
                }
            }
            meuDiv.style.left = meuDivLeft + "px";
        }
    </script>
</head>
<body>
    <!-- apenas um exemplo -->
    <div id="meuMenu" style="position: relative; height: 45px; background: #00f; color: #fff;">
        <span>Item A</span> | <span>Item B</span> | <span>Item C</span> | <span>Item D</span>
        <div id="meuDiv" style="position: absolute; width: 45px; height: 5px; bottom: 0px; background: #fff;"></div>
    </div>
    <script type="text/javascript">
        meuMenu = document.getElementById("meuMenu");
        meuDiv = document.getElementById("meuDiv");
        //armazena left de meuMenu, para evitar ficar utilizando a função elementLeft toda a vez
        meuMenuLeft = elementLeft(meuMenu);
        //trata o evento do movimento do cursor na fase de captura
        meuMenu.addEventListener("mouseenter", meuMenu_MouseEnter, true);
        meuMenu.addEventListener("mousemove", meuMenu_MouseMove, true);
    </script>
</body>
</html>
    
13.05.2014 / 17:46
2

Rodrigo, here is my suggestion:

Example: link

$(document).ready(function () {
    var marcador = $('.MarcadorMenu');
    var LeftInicial = marcador.css('left');
    var WidthInicial = marcador.css('width');
    var entrou = false;
    $('.CorpoMenuHead').on('mousemove', function (event) {
        console.log(entrou, event.pageX);
        if (!entrou) {
            marcador.stop().animate({
                left: (event.pageX - LeftInicial.replace('px', '')) + 'px'
            },500, function(){
                entrou = true;
            });
        }else{
        marcador.css('left', (event.pageX - LeftInicial.replace('px', '')) + 'px');
        }

    });

    $('.MenuHead').on('mouseleave', function () {
        entrou = false;
        marcador.stop().animate({
            left: LeftInicial,
            width: WidthInicial
        });
    });
});

I changed the hover by mouseenter / mouseleave. In this case I use only the mouseleave since the mousemove already confirms that the mouse is on the element. The problem with the hover is that it triggers too many events.

I also took bind () from within the move, the risk is that the DOM is adding event handlers every time the hover fires, and this is not desirable.

I've added .stop() to the animations so they do not wait for each other but stop and restart if a new animate is triggered.

I used .on () instead of .bind () by jQuery itself for later versions of 1.7

    
13.05.2014 / 13:11