Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Snake-alike fluid layout algorithm

The goal is to produce a fluid layout as show below.

enter image description here

So far I have a working function moveBox(lastBox, "east") that keeps track of the row and column indexes.

function moveBox(box, where) {
  switch (where) {
    case "north":
      lastTopOffset -= BOX_HEIGHT + BOX_MARGIN;
      box.style.top  = lastTopOffset  + 'px';
      box.style.left = lastLeftOffset + 'px';
      rowIndex -= 1;
      break;
    //  ...    
  }

My current code,

(function () {
    var i, lastBox,
      MAX_DIVS       = 72,
      BOX_HEIGHT     = 50,
      BOX_WIDTH      = 100,
      BOX_MARGIN     = 5,
      field          = document.getElementById('fieldPerimeter'),
      fieldHeight    = field.offsetHeight,
      maxRows        = Math.floor(fieldHeight / (BOX_HEIGHT + BOX_MARGIN)),
      rowIndex       = 0,
      colIndex       = 0,
      lastLeftOffset = 0,
      lastTopOffset  = 0;

  function moveBox(box, where) {
    switch (where) {
      case "north":
        lastTopOffset -= BOX_HEIGHT + BOX_MARGIN;
        box.style.top  = lastTopOffset  + 'px';
        box.style.left = lastLeftOffset + 'px';
        rowIndex -= 1;
        break;

      case "east":
        lastLeftOffset += BOX_WIDTH + BOX_MARGIN;
        box.style.top  = lastTopOffset  + 'px';
        box.style.left = lastLeftOffset + 'px';
        colIndex += 1;
        break;

      case "south":
        lastTopOffset += BOX_HEIGHT + BOX_MARGIN;
        box.style.top  = lastTopOffset  + 'px';
        box.style.left = lastLeftOffset + 'px';
        rowIndex += 1;
        break;

      default:
        break;
    }
  }

  for (i = 0; i < MAX_DIVS; i += 1) {
    lastBox = document.createElement('div');
    lastBox.className = 'box';
    lastBox.innerHTML = i;
    field.appendChild(lastBox);      

    //delete me 
    if( (i + 1) % 2 === 0 || (i + 1)% 3 === 0){ 
      moveBox(lastBox, "east");
    } else {
      moveBox(lastBox, "south");
    }
    //delete me      

//    if(rowIndex < maxRows && rowIndex > 0){
//    if (colIndex % 4 === 0){
//      moveBox(lastBox, "south");
//    } else if (colIndex % 2 === 0){
//      moveBox(lastBox, "north");
//    } else {
//     moveBox(lastBox, "east");
//    }
//  } 

  }      
})();

appends divs to a container and then moves it. The code below shows part of my attempts for specifing when to move North or South. But I'm struggling with achiving the desired layout.

 if      (colIndex % 4 === 0) { moveBox(lastBox, "south"); } 
 else if (colIndex % 2 === 0) { moveBox(lastBox, "north"); }
 else                         { moveBox(lastBox, "east");  }
like image 261
user1032739 Avatar asked Nov 06 '11 21:11

user1032739


2 Answers

enter image description here

Here is the working fiddle, http://jsfiddle.net/efortis/zuY74/

Note I hardcoded the offsetHeight in order to work on the fiddle and also added a lastMove variable at the top.

  for (i = 0; i < MAX_DIVS; i += 1) {
    lastBox = document.createElement('div');
    lastBox.className = 'box';
    lastBox.innerHTML = i;
    field.appendChild(lastBox);

    if (i === 0) {
      rowIndex += 1;
    } else {
      if (colIndex % 4 === 0 && rowIndex < maxRows) {
        moveBox(lastBox, "south");
        lastMove = "south";
      } else if (colIndex % 2 === 0 && rowIndex !== 1 && lastMove !== "south") {
        moveBox(lastBox, "north");
        lastMove = "north";
      } else {
        moveBox(lastBox, "east");
        lastMove = "east";
      }
    }
  }
like image 73
Eric Fortis Avatar answered Oct 16 '22 20:10

Eric Fortis


The following works with grid positions rather than pixels, the idea being that you can convert a grid position to pixels without trouble.

My grid is pretty simple. The upper left is (0, 0). So your box 0 is at (0, 0), box 7 is at (1, 6), etc.

If you have maxrows rows, then the first maxrows-1 items go in (0, 0), (0, 1), etc. And item maxrows goes in (1, maxrows-1). The next maxrows-1 items go in (maxrows-1,2), (maxrows-2, 2), etc., and the item at 2*maxrows goes in (0, 3).

You should be able to compute an item's position in the grid from its number. Assume integer math here.

// xblock treats each two columns as a single entity.
xblock = itemNo / (maxrows + 1);
xpos = 2 * xblock;
ypos = itemNo % (maxrows + 1);
if (ypos == maxrows)
{
    // this is the last item, so we need to shift it.
    xpos += 1;
    ypos = maxrows - 1;
}

// Now, turn things upside down if xblock is odd
if ((xblock % 2) == 1 && ypos != maxrows)
{
    ypos = maxrows - ypos - 1;
}

At this point you have the grid position where the box should go. It should be a simple matter now to turn that grid position into pixels by multiplying the xpos by BOX_WIDTH, and adding the offset. Do the same thing for ypos and BOX_HEIGHT.

like image 2
Jim Mischel Avatar answered Oct 16 '22 20:10

Jim Mischel