最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Snake-alike fluid layout algorithm - Stack Overflow

programmeradmin7浏览0评论

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

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");  }

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

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");  }
Share Improve this question edited Nov 6, 2011 at 21:32 Bart Kiers 170k37 gold badges306 silver badges295 bronze badges asked Nov 6, 2011 at 21:15 user1032739user1032739 1534 bronze badges 3
  • 6 A fiddle, image demonstration, and code (thanks to Bart Kiers) on an interesting question from a new user? Upvote. I wish all new users had this going for them. :) – Jared Farrish Commented Nov 6, 2011 at 21:35
  • Is the way you've approached it a requirement, or just the one you've tried? – Jared Farrish Commented Nov 6, 2011 at 21:47
  • I've tried other approaches, I've posted it because I think I'm close but my head explodes trying to figure out the algorithm. Later I want to use a random value for not going to the very North or South, but for now I just need some guiding with some pseudocode. Thanks Mr. Kiers for the edit. – user1032739 Commented Nov 6, 2011 at 21:58
Add a ment  | 

3 Answers 3

Reset to default 5

Here is the working fiddle, http://jsfiddle/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";
      }
    }
  }

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 pute 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.

I noticed that is a recurring pattern in the layout: Starting from 0

  • move box down 6 times (reach position 6)
  • move box right 2 times (reach 8)
  • move box up 6 times (reach 14)
  • move box right 2 times (reach 16)
  • repeat

-

var MOVES_DONE = 0;
var MOVES_LIMIT = 72;

/*
* First define the three movement functions that we will use 
* (right, down and left)
*/
function up(box) {
    console.log("up");
}

function down(box) {
    console.log("down");
}

function right(box) {
    console.log("right");
}

/*
*   Solution 1:
*   Starting from the top-left corner do the necessary moves to plete the 
*   layout:

          ---------------------------------
        |                                     |
         box,             up,   right,  right,
        down,             up,            start_over
        down,             up, 
        down,             up,
        down,             up,
        down,             up,       
        down,   right,   right, 
*/

var moves = [down, down, down, down, down, down, right, right,
             up, up, up, up, up, up, right, right];

var len_moves = moves.length;

while(MOVES_DONE < MOVES_LIMIT) {
    moves[MOVES_DONE % len_moves](box);
    MOVES_DONE ++;
}

Solution 2 using the same movement functions:

/**
*   Create a function that will apply a movement type to a box "times" times
*   For example move(down, 6)(box) will
*   move the box down 6 times if moves_done < moves_limit
*/
function move(move_type_function, times) {
    return function(box) {
        for (var i = 0; i < times, i + MOVES_DONE < MOVES_LIMIT; i++) {
            move_type_function(box);
      }
        return i;
   }
}

/**
* This is a plete cycle of the recurring pattern of the layout assuming that
* we are starting with the box positioned at the 
*/
var moves = [move(down, 6), move(right, 2), move(up, 6), move(right, 2)];

while(MOVES_DONE < MOVES_LIMIT) {
    MOVES_DONE += moves[MOVES_DONE % moves.length](box)
}

PS: I haven't really had time to test this in a browser so there might be some errors :)

发布评论

评论列表(0)

  1. 暂无评论