Hello group,

I'm trying to accomplish a behaviour where multiple draggables cannot
overlap, i.e. you can drag them around freely, but they "snap" to each
other's borders so that you cannot place one draggable over another.
I've come close to a solution, but it's only 95% what I'd like to have
and it seems overly complicated to me.

The solution I've come up with so far is to grab each element already
dropped into my droppable, read its offsets and save it as a range.
Then, when you drag around one element, it checks for the saved ranges
if an overlap occurs on any axis. If yes, it sets the x or y
coordinate outside of the not-dragged element's boundaries. Or at
least that what it should do.

The problem is, I cannot distinguish between if the dragged element
should be placed atop/below or left/right of the immobile one.
More specifically, I don't know if the direction of the drag is in the
X or in the Y-axis.
Let me try some obscure ASCII art:

Direction bottom -> top:
.-----.
| fix |
'-----'
    .-----.
    | ^^^ |
    '-----'

Direction right -> left:
.-----.
| fix |.-----.
'-----'| <<< |
       '-----'

Outcome:
.-----.
|   .-|---.
'---|-'   |
    '-----'


On both occasions, when the elements finally overlaps the fixed
element, its top value AND its left value are inside the "ranges" of
the fixed element, without knowing what axis should be limited. Move
it to the bottom? Move it to the right?

As you can see below, the function to determine wether the element is
within the boundaries of another seems clunky and complicated, so I
was hoping for a thought provoking impulse on how to solve that last
puzlle, or maybe even a new way to approach this with lesser effort.

I've fiddled around with Position.overlap and Position.within, but
both are deemed deprecated (what is the proper way then now?), and
this approach also suffered from the same indistinguishability (did I
just write this?).

This is my code so far. Stripped and ripped out of its class.

** HTML **

<div id='container'>
 <div class='dragme'>Item 1</div>
 <div class='dragme'>Item 2</div>
 <div class='dragme'>Item 3</div>
</div>

<div id='target'></div>



** JavaScript **

var container  = $('container');
var dropTarget = $('target');
var taken      = new Array();
var curEleDims = {};
var dropBorder = 2;

var drags = container.select('div.dragme');
drags.each(function(ele) {
  new Draggable(ele, { revert: 'failure', ghosting: false, onStart:
initializeDrag });
});

Droppables.add(container, {accept: 'dragme', onDrop: moveDropped });


// Set options at begin of drag
initializeDrag = function(drag) {

  // If already within dropTarget
  if (drag.element.parentNode.id == dropTarget.id) {
    taken = new Array(); // Clear Array

    // Every element already dropped
    dropTarget.childElements().each(function(ele) {

      // Ignore current element (the drag itself)
      if (drag.element.id !== ele.id) {
        var dims, oSet, x1, x2, y1, y2;

        dims = ele.getDimensions(); // Get dimensions
        oSet = ele.positionedOffset(); // For Range
        x1 = oSet.left; x2 = oSet.left + dims.width; // Left & Right
        y1 = oSet.top;  y2 = oSet.top  + dims.height; // Top & Bottom
        taken.push([$R(x1, x2, false), $R(y1, y2, false)]);
      }
    });

    drag.options.snap = snap; // Limit Snap
    curEleDims = drag.element.getDimensions(); // Dimensions of
current element
  }
}

// Drop a draggable
moveDropped = function(drag, drop) {

  // Container => drop target
  if (drag.parentNode.id == container.id && drop.id == dropTarget.id)
{

    // Convert to position: absolute and change offset accordingly
    // Needed when dropTarget has a border, else drag "jumps" on
release
    drag.absolutize();

    var offTotalPar = drop.cumulativeOffset();
    var offTotalEle = drag.cumulativeOffset();

    // Include 2x 2px border
    drag.setStyle({
      top:  offTotalEle.top -offTotalPar.top -dropBorder*2+'px',
      left: offTotalEle.left-offTotalPar.left-dropBorder*2+'px'
    });

    // Insert to new element on drop
    drop.insert(drag);
  }
}


// Snap within borders of dropTarget and no overlap between elements
snap = function(x,y,draggable) {
  var nx, ny, takenXX, takenYY, curXX, curYY;

  // Limit to the boundaries of dropTarget only
  /*
    // already working
    [...]
  */


  // Limit taking into account already dropped elements

  // Ranges for current element
  curXX = $R(x, x+curEleDims.width,  false); // Left & Right
  curYY = $R(y, y+curEleDims.height, false); // Top & Bottom

  // Coordinates already occupied?
  taken.each(function(xyVal) { // For each dropped element
    var x1, x2, y1, y2;

    // Ranges for an element already dragged and dropped
    takenXX = xyVal[0]; // Left & Right
    takenYY = xyVal[1]; // Top & Bottom

    x1 = x; // Left
    x2 = x + curEleDims.width); // Right
    y1 = y; // Top
    y2 = y + curEleDims.height; // Bottom

    // Point of view is the currently dragged element
    var left_side, right_side, left_top, left_btm, right_top,
right_btm;

    left_side  = (takenXX.include(x1) &&
                  && ((takenYY.include(y1) && takenYY.include(y2))
                  || (curYY.include(takenYY.start)
                       && curYY.include(takenYY.end)))
                 ) ? true : false;
    right_side = (takenXX.include(x2)
                  && ((takenYY.include(y1) && takenYY.include(y2))
                  || (curYY.include(takenYY.start)
                       && curYY.include(takenYY.end)))
                 ) ? true : false;

    left_top   = (takenYY.include(y1)
                  && (takenXX.include(x1)
                      || (curXX.include(takenXX.start)))
                 ) ? true : false;
    left_btm   = (takenYY.include(y2)
                  && (takenXX.include(x1)
                      || (curXX.include(takenXX.start)))
                 ) ? true : false;

    right_top  = (takenYY.include(y1)
                  && (takenXX.include(x2)
                      || (curXX.include(takenXX.end)))
                 ) ? true : false;
    right_btm  = (takenYY.include(y2)
                  && (takenXX.include(x2)
                      || (curXX.include(takenXX.end)))
                 ) ? true : false;

    switch (true) {
      // Place at right end
      case left_side:  x = takenXX.end; break;

      // Place at left end
      case right_side: x = takenXX.start-curEleDims.width; break;

      // Place at bottom (or left?)
      case left_top:   y = takenYY.end; break;

      // Place at top (or left?)
      case left_btm:   y = takenYY.start-curEleDims.height; break;

      // Place at bottom (or right?)
      case right_top:  y = takenYY.end; break;

      // Place at top (or right?)
      case right_btm:  y = takenYY.start-curEleDims.height; break;
    }
    if (left_side || right_side || left_top || left_btm || right_top
|| right_btm) return;  // No need to proceed further

  });


  return [x,y];
}

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to