16

I’m looking for a way to simulate resistance using the jQueryUI draggable plugin (similar to this effect). At the bottom of the draggable documentation it mentions:

“To manipulate the position of a draggable during drag, you can either use a wrapper as the draggable helper and position the wrapped element with absolute positioning, or you can correct internal values like so: $(this).data('draggable').offset.click.top -= x”.

Geometry not being my strong suit I was looking for help on how to best achieve the effect of resistance when dragging something. I thought that using this tip above, I could change the distance the draggable is moved using a geometric function. I’m not sure if the best term is resistance or elasticity, but I’m looking for the feel as if an element is attached to a point by a rubber band or bungee cord so that the further you drag, the less the object moves.

For example, say I want to drag an object a total distance of 500 pixels (in any direction). I would like the resistance effect to increase the closer to 500 pixels away from the starting point I get. I’ve looked around and haven’t seen anything like this.

Update:

I created a basic jsFiddle that calculates the distance an item has been dragged at http://jsfiddle.net/Z8m4B/

The calculation is:

var x1=x2=y1=y2=0;
$("#draggable").draggable({
    start: function(e, ui) {
        y1 = ui.position.top;
        x1 = ui.position.left;
    },
    stop: function(e, ui) {
        y2 = ui.position.top;
        x2 = ui.position.left;        
        dist = parseInt(Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2)), 10);
        console.log(dist);
    }
});

Obviously I would want to change the distance during the drag event and not on stop. Does anyone know how a function to create this resistance or stretch effect?

1

4 Answers 4

16
+100

you can try with this http://jsfiddle.net/sAX4W/ with the drag event you can calculate the distance and get a % from the real distance

var x1 = x2 = y1 = y2 = 0;
$("#draggable").draggable({
    revert: true,
    revertDuration: 100,
    axis: 'y',
    drag: function(e, ui) {
        y2 = ui.position.top;
        x2 = ui.position.left;
        dist = parseInt(Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2)), 10);
        ui.position.top = ui.position.top * (1 - (dist / 1000));
    },
    start: function(e, ui) {
        y1 = ui.position.top;
        x1 = ui.position.left;
    },
    stop: function(e, ui) {
    }
});​

edit

you can try this with both axis http://jsfiddle.net/2QndJ/

var x1 = x2 = y1 = y2 = 0;
$("#draggable").draggable({
    revert: true,
    revertDuration: 100,

    drag: function(e, ui) {
        y2 = ui.position.top;
        x2 = ui.position.left;
        dist = parseInt(Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2)), 10);

        ui.position.top = ui.position.top * (1 - (dist / 1000));
        ui.position.left = ui.position.left * (1 - (dist / 1000));
    },
    start: function(e, ui) {
        y1 = ui.position.top;
        x1 = ui.position.left;
    },
    stop: function(e, ui) {
    }
});​
1
  • If you drag too far, it element starts moving in the opposite direction of your drag. I.e. it follows, then slows, stops, and starts moving backwards.
    – Flambino
    Commented Mar 8, 2012 at 9:24
9

Here's a quick jsfiddle

Basically, it uses the simple formula 1 / (x+1). That's an asymptotic curve, meaning that y → 0 for x → infinity (for x >= 0). So y is forever approaching zero, but never gets there.

x in this case is the distance the mouse has been dragged, and y is used to scale the drag vector. So the further the mouse moves, the less the draggable will move, as y gets closer and closer to zero.

So if the mouse is dragged only a little, the draggable pretty much keeps up. But the farther you drag it, the less it will follow.

asymptote

By the way, it looks like the example you linked to simply halves the dragged distance. So if you drag it 100 pixels, it moves 50 pixels. That's a simpler solution, but a different behavior, since it's linear. My solution is makes the draggable move exponentially less the farther you (try to) drag it, so it's a different kind of behavior.


jQuery

var halfDist = 150, // the mouse distance at which the mouse has moved
                    // twice as far as the draggable

centerX = 150,      // where to anchor the draggable
centerY = 150;

$("#draggable").draggable({
    drag: function(event, ui) {
        var x, y, dist, ratio, factor;
        x = ui.position.left - centerX;
        y = ui.position.top - centerY;
        dist = Math.sqrt(x*x + y*y);
        ratio = dist / halfDist
        factor = 1 / (ratio + 1);
        x *= factor;
        y *= factor;
        ui.position.left = x + centerX;
        ui.position.top  = y + centerY;
    }
});​

HTML

<div id="arena">
    <div id="draggable"/>
</div>​

CSS

#arena {
    position: relative;
    width: 300px;
    height: 300px;
    border: 1px solid #ccc;
}

#draggable {
    width: 20px;
    height: 20px;
    background: red;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-left: -10px;
    margin-top:  -10px;
}

Thanks to j08691 for taking the code from jsfiddle and placing it here too

3
  • Seems to have some jumping issues. You've got the general idea though.
    – j08691
    Commented Mar 1, 2012 at 20:47
  • Yes, it jumps when you start dragging again, because your mouse has moved closer to the center to click it again. However, I'll leave that as an exercise to the reader for now, since a) I've got work to do, and b) I don't want to do all your work for you ;) May get back to it later, though, but no promises
    – Flambino
    Commented Mar 1, 2012 at 20:55
  • @AstDerek Nice - although I'm not sure if the original question calls for the the element always being anchored to the same place, and not just the drag-start position. But if not, then you got it right
    – Flambino
    Commented Mar 7, 2012 at 19:46
3
var x1=x2=y1=y2=0;
var maxdistance=1000;
var factor=2;
$(function() {
    $("#draggable").draggable({
        helper: function(){
            return $('<div></div>').css('opacity',0);
        },
        start: function(e, ui) {
            y1 = ui.position.top;
            x1 = ui.position.left;
        },
        stop: function(){
            var $this = $(this);
            $this.stop().animate({
                top: $this.data('starttop')
            },1000,'easeOutCirc');
        },
        drag: function(event, ui){
            y2 = ui.position.top;
            x2 = ui.position.left;        
            dist = parseInt(Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2)), 10);
            ydist = (y2-y1);
            xdist = (x2-x1);
            $("#draggable").css("top", (y1+ydist*((maxdistance-ydist)/maxdistance)));
            $("#draggable").css("left", (x1+xdist*((maxdistance-xdist)/maxdistance)));
        }
    });
});​

http://jsfiddle.net/Lf5vc/

1
  • This is close, however when I stop dragging and start again, the draggable item seems to orient itself to the spot it was dropped at instead of the original starting position.
    – j08691
    Commented Mar 1, 2012 at 0:26
0

There are two ways: You can get the left value of the element:

drag: function(event, ui ) {
  distance = $(element).css("left")
}

or you can calculate it:

start: function( event , ui ) {
    globalX = event.clientX
},

drag: function( event, ui ) {
    distance = Math.abs(event.clientX - globalX);
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.