类似蛇的流体布局算法

时间:2011-11-06 21:15:34

标签: javascript html css algorithm

目标是生成流体布局,如下所示。

enter image description here

到目前为止,我有一个工作函数moveBox(lastBox, "east"),可以跟踪行索引和列索引。

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;
    //  ...    
  }

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

  }      
})();

将div附加到容器然后移动它。下面的代码显示了我指定何时移动北或南的部分尝试。但我正在努力实现理想的布局。

 if      (colIndex % 4 === 0) { moveBox(lastBox, "south"); } 
 else if (colIndex % 2 === 0) { moveBox(lastBox, "north"); }
 else                         { moveBox(lastBox, "east");  }

3 个答案:

答案 0 :(得分:5)

enter image description here

这是工作小提琴,http://jsfiddle.net/efortis/zuY74/

注意我为了处理小提琴而对offsetHeight进行了硬编码,并在顶部添加了lastMove变量。

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

答案 1 :(得分:2)

以下适用于网格位置而不是像素,我们的想法是您可以毫无困难地将网格位置转换为像素。

我的网格非常简单。左上角是(0, 0)。因此,您的方框0位于(0, 0),方框7位于(1, 6)等,

如果您有maxrows行,则第一个maxrows-1项会进入(0, 0)(0, 1)等。项maxrows进入{{1} }}。下一个(1, maxrows-1)项目会显示maxrows-1(maxrows-1,2)等,而(maxrows-2, 2)的项目会显示在2*maxrows

您应该能够从数字中计算项目在网格中的位置。在这里假设整数数学。

(0, 3)

此时,您将拥有框应该移动的网格位置。现在通过将// 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; } 乘以xpos并添加偏移量将网格位置转换为像素应该是一件简单的事情。对BOX_WIDTHypos执行相同的操作。

答案 2 :(得分:0)

我注意到这是布局中反复出现的模式: 从0开始

  • 将箱子向下移动6次(到达位置6)
  • 向右移动框2次(达到8)
  • 将盒子移动6次(达到14)
  • 右移2次(达到16)
  • 重复

-

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 complete 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 ++;
}

解决方案2使用相同的运动功能:

/**
*   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 complete 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:我没有时间在浏览器中测试这个,所以可能会有一些错误:)