Search code examples
javascriptjqueryjquery-uijquery-animatedraggable

jQuery - how to animate draggable div towards moving cursor during drag event


I am using jquery UI draggable and want to have a draggable div animate during a drag event. Imagine that when stationary, the square div in the example is attached to a magnet. Wen you click on it and start dragging, it will not drag until a certain distance threshold is reached (distance = distance between center of div and current mouse position). After that threshold is reached, the div will animate toward the mouse and should then proceed to a normal drag event.

My problem is that when the mouse is dragged past the distance threshold at a fast enough speed, the div will start flashing because it is switching between showing the animated div and the one that is being dragged. Another problem that occurs is that if you drag the mouse fast enough and then stop, the div animates to the last recorded mouse position, but by the time the calculation is made, the mouse might already be at a different position, so the mouse is at one spot and the div is at another. Any ideas on how to make the div animate towards the mouse and then continue the drag event in one smooth transition? (also, i want the animation do be long enough so that you can see the div moving. if duration is set to 1, it works fine but I want it to be more visually appealing and smooth) Here is a demo: http://jsfiddle.net/b84wn2nf/

Here is some of the code found in the demo:

$(".dragSquare").draggable({
drag: function (event, ui) {

    if($(this).hasClass("stuck")){
    var mx = event.pageX;
    var my = event.pageY;
    ui.position.left = $(this).position().left;
    ui.position.top = $(this).position().top;

    var d = Math.floor(Math.sqrt(Math.pow(mx - ($(this).offset().left + ($(this).width() / 2)), 2) + Math.pow(my - ($(this).offset().top + ($(this).height() / 2)), 2)));
    console.log(d)
    if (d > 200) {
        var animateToX = event.pageX - $(this).width() / 2;
        var animateToY = event.pageY - $(this).height() / 2;

        $(this).stop(true, true).animate({
            left: animateToX + 'px',
            top: animateToY + 'px'
        }, {
            /*easing: 'easeOutBounce,'*/
            duration: 500,
            start: function () {},
            complete: function () {
                $(this).removeClass("stuck");
            }
        });
    }
    }
} });

Solution

  • Okay, So I know I posted this a long time ago, and since then, I started to use the Snap.js SVG library instead of jQuery, which makes it much easier to cancel a drag, but before the switch, I solved the problem the only way I could: by modifying the source code.

    In jquery-ui.js, locate the jQuery UI Draggable section, and then scroll down to _mouseDrag method. What you need to do in your own code is set a global variable to tell jQuery if you want the drag behavior to be overridden. I used 'cancelDrag' as the variable name. So when its set to false, dragging behaves normally.

    In _mouseDrag, you will see the following code:

    this.helper[0].style.left = this.position.left + "px";
    this.helper[0].style.top = this.position.top + "px";
    

    What you need to do is wrap it in a conditional statement that depends on your boolean variable, so it looks like this:

    if(!cancelDrag){
        this.helper[0].style.left = this.position.left + "px";
        this.helper[0].style.top = this.position.top + "px";
    }
    

    Basically, if cancelDrag is set to true, jQuery drag handler will not change the position of your element. Its not ideal, and should probably not be used, but it works. Make sure that if you modify this file, you are not using the minified source.