views:

842

answers:

5

Hello everyone!

I am wondering if there is a way to make the jQuery draggable to only drag straight up, down and left, right. I want to prevent the user from dragging a div diagonally. Using the grid option in the draggable UI is not possible in my situation.

http://jqueryui.com/demos/draggable/#constrain-movement

How is this possible?

Thanks!

A: 

I don't have exact code for you, but I might be able to point you in the right direction by helping you to think about the problem a little bit more deeply.

So the user begins to drag an unconstrained object. As the object moves one pixel away from the starting point, if the first pixel is at any of these four (x,y) coordinates, where x and y are the starting point: (x-1,y-1), (x-1,y+1),(x+1,y-1) or (x-1,y+1), it is still unclear which way the user means to go, For instance, if the object is at one down and on left, it's impossible to tell whether the constraint should be on the x or y axis.

Once you move more than one pixel away, it gets easier. But you still have to do some rounding on the pixel coordinates where, as in the coordinates above, abs(xoffset) == abs(yoffset), Then you can fire an event to apply the constraint when the x and/or y distance are greater than some arbitrary small integer, say "3".

The next problem to deal with is how far from the last firing of the axis constraint do you want to allow the user to change the axis. I'm guessing that you want a grid effect, so you'll have to set a number-of-pixels moved threshold before you remove the constraint and look for the next "direction" that the user wants to drag.

This is an interesting challenge and a good learning experience if you figure out how to do it.

A: 
Matt Ball
The image isn't showing correctly because of hotlinking.
Reinis I.
Sorry! I think the image was actually a little corrupted. The new one should work.
Matt Ball
A: 

use the drag event to your advantage

$('.selector').draggable({
   drag: function(event, ui) { ... }
});

i'm not 100% sure how, but inside here, you can do a check on the coordinates. as ~jack-laplante said, this is where you would compare. if the coordinates are closer to the x axis than the y, change the axis to x

//setter
$('.selector').draggable('option', 'axis', 'x');

if the other way around change it to y. this will get you a snapping motion to always have the item either on the x axis or the y axis (from the starting point). you'd have to do a check to leave the item wherever it is if the axis was say (x+n,y+n), but it would be pretty impressive if someone got more than a few pixels staying on the |x|=|y| line.

you didn't specify if the axes always remain where they originally started or if they change depending on where the draggable item is on drag start (like if you move it a little bit, then let go, and try to drag it some more). there's a start: and end: event too that could help you with that part.

what exactly are you trying to drag that can't go diagonal, but can go left, right, up, and down?

Brandon H
Thanks for the input! I am creating an image gallery where the user are going to drag an image to move to the next picture. If the user drags the image up or down, a small info text will be displayed.
Fred Bergman
A: 

I'm posting this answer after much fiddling to say that it's not possible with just jqueryui calls on the object. It seems that it will not allow you to change the restraint axis while you are already dragging.

Please prove me wrong?

snicker
+2  A: 

Wrote a plugin that allows moving a draggable in both axes. I gets the job done but it would be better-implemented as a jQuery UI widget rather than a simple jQuery plugin.

Hosted Demo: http://jsbin.com/ugadu (Editable via http://jsbin.com/ugadu/edit)

Plugin code:

$.fn.draggableXY = function(options) { 
  var defaultOptions = { 
    distance: 5, 
    dynamic: false 
  }; 
  options = $.extend(defaultOptions, options); 

  this.draggable({ 
    distance: options.distance, 
    start: function (event, ui) { 
      ui.helper.data('draggableXY.originalPosition', ui.position || {top: 0, left: 0}); 
      ui.helper.data('draggableXY.newDrag', true); 
    }, 
    drag: function (event, ui) { 
      var originalPosition = ui.helper.data('draggableXY.originalPosition'); 
      var deltaX = Math.abs(originalPosition.left - ui.position.left); 
      var deltaY = Math.abs(originalPosition.top - ui.position.top); 

      var newDrag = options.dynamic || ui.helper.data('draggableXY.newDrag'); 
      ui.helper.data('draggableXY.newDrag', false); 

      var xMax = newDrag ? Math.max(deltaX, deltaY) === deltaX : ui.helper.data('draggableXY.xMax'); 
      ui.helper.data('draggableXY.xMax', xMax); 

      var newPosition = ui.position; 
      if(xMax) { 
        newPosition.top = originalPosition.top; 
      } 
      if(!xMax){ 
        newPosition.left = originalPosition.left; 
      } 

      return newPosition; 
    } 
  }); 
};
brianpeiris
This seems interesting but your link doesn't work. Can't find out why...
Fred Bergman
Argh! Sorry, had a debugging line in there that broke it unless you had Firebug open. I've fixed it and changed the link in the answer.
brianpeiris