Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery ui drag easing/inertia

How do you enable easing or inertia when dragging an element using jquery ui draggable (http://jqueryui.com/demos/draggable/)? I'd like to recreate the easing similar to maps.google.com that as you throw/drag the map it eases into place. Ideally, I'd like to move the element move based on the force you throw/drag the element. How do you accomplish this functionality? Maybe jquery ui draggable isn't necessary, but I'm looking to emulate the dragging and easing found on Google Maps.

Thanks!

like image 715
jaysonp Avatar asked Dec 14 '10 19:12

jaysonp


2 Answers

I used some ideas from here but integrated them with jQuery UI instead. You'll have to implement logic to handle a momentum animation that pushes the element out-of-bounds (outside it's parent container's boundaries)

The resulting code:

$(function() {
    var $d = $("#draggable");

    var x1, x2,
        y1, y2,
        t1, t2;  // Time

    var minDistance = 40; // Minimum px distance object must be dragged to enable momentum.

    var onMouseMove = function(e) {
        var mouseEvents = $d.data("mouseEvents");
        if (e.timeStamp - mouseEvents[mouseEvents.length-1].timeStamp > 40) {
            mouseEvents.push(e);
            if (mouseEvents.length > 2) {
                mouseEvents.shift();
            }
        }
    }

    var onMouseUp = function() {
        $(document).unbind("mousemove mouseup");
    }

    $d.draggable({
        start: function(e, ui) {
            $d.data("mouseEvents", [e]);
            $(document)
                .mousemove(onMouseMove)
                .mouseup(onMouseUp);
        },
        stop: function(e, ui) {
            $d.stop();
            $d.css("text-indent", 100);

            var lastE = $d.data("mouseEvents").shift();

            x1 = lastE.pageX;
            y1 = lastE.pageY;
            t1 = lastE.timeStamp;
            x2 = e.pageX;
            y2 = e.pageY;
            t2 = e.timeStamp;

            // Deltas
            var dX = x2 - x1,
                dY = y2 - y1,
                dMs = Math.max(t2 - t1, 1);

            // Speeds
            var speedX = Math.max(Math.min(dX/dMs, 1), -1),
                speedY = Math.max(Math.min(dY/dMs, 1), -1);

            // Distance moved (Euclidean distance)
            var distance = Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));

            if (distance > minDistance) {
                // Momentum
                var lastStepTime = new Date();
                $d.animate({ textIndent: 0 }, {
                    duration: Math.max(Math.abs(speedX), Math.abs(speedY)) * 2000,
                    step: function(currentStep){
                        speedX *= (currentStep / 100);
                        speedY *= (currentStep / 100);

                        var now = new Date();
                        var stepDuration = now.getTime() - lastStepTime.getTime();

                        lastStepTime = now;

                        var position = $d.position();

                        var newLeft = (position.left + (speedX * stepDuration / 4)),
                            newTop = (position.top + (speedY * stepDuration / 4));

                        $d.css({
                            left: newLeft+"px",
                            top: newTop+"px"
                        });
                    }
                });
            }
        }
    });
});

Try it out

like image 67
simshaun Avatar answered Oct 03 '22 08:10

simshaun


The work simshaun did on this is fantastic.

I messed with his version and got a bit smoother animation with the jquery.easing plugin.
Try it out on jsfiddle.

$(document).ready(function() {
    $('#dragme').draggable({
        start: function(e, ui) {
            dragMomentum.start(this.id, e.clientX, e.clientY, e.timeStamp);
         },
        stop: function(e, ui) {
            dragMomentum.end(this.id, e.clientX, e.clientY, e.timeStamp);
        }  
     });
});

var dragMomentum = new function () {    
    var howMuch = 30;  // change this for greater or lesser momentum
    var minDrift = 6; // minimum drift after a drag move
    var easeType = 'easeOutBack';

    //  This easing type requires the plugin:  
    //  jquery.easing.1.3.js  http://gsgd.co.uk/sandbox/jquery/easing/

    var dXa =[0];
    var dYa =[0];
    var dTa =[0];

    this.start = function (elemId, Xa, Ya, Ta)  {
          dXa[elemId] = Xa;
        dYa[elemId] = Ya;
        dTa[elemId] = Ta;

      }; // END dragmomentum.start()

    this.end = function (elemId, Xb, Yb, Tb)  {        
        var Xa = dXa[elemId];
        var Ya = dYa[elemId];
        var Ta = dTa[elemId];
        var Xc = 0;
        var Yc = 0;

        var dDist = Math.sqrt(Math.pow(Xa-Xb, 2) + Math.pow(Ya-Yb, 2));
        var dTime = Tb - Ta;
        var dSpeed = dDist / dTime;
        dSpeed=Math.round(dSpeed*100)/100;

        var distX =  Math.abs(Xa - Xb);
        var distY =  Math.abs(Ya - Yb);

        var dVelX = (minDrift+(Math.round(distX*dSpeed*howMuch / (distX+distY))));
        var dVelY = (minDrift+(Math.round(distY*dSpeed*howMuch / (distX+distY))));

        var position = $('#'+elemId).position();
        var locX = position.left;
        var locY = position.top;

        if ( Xa > Xb ){  // we are moving left
            Xc = locX - dVelX;
        } else {  //  we are moving right
            Xc = locX + dVelX;
        }

        if ( Ya > Yb ){  // we are moving up
            Yc = (locY - dVelY);
        } else {  // we are moving down
            Yc = (locY + dVelY);
        }

        var newLocX = Xc + 'px';
        var newLocY = Yc + 'px';

        $('#'+elemId).animate({ left:newLocX, top:newLocY }, 700, easeType );

    }; // END  dragmomentum.end()

};  // END dragMomentum()
like image 37
mattsahr Avatar answered Oct 03 '22 06:10

mattsahr