Алгоритм компоновки жидкости с помощью Snake

Цель состоит в том, чтобы создать жидкую компоновку, как показано ниже.

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

Мой текущий код,

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

  }      
})();

добавляет divs в контейнер и затем перемещает его. В приведенном ниже коде показана часть моих попыток указать, когда нужно перемещать север или юг. Но я борюсь с достижением желаемого макета.

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

Ответы

Ответ 1

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

Ответ 2

Следующее работает с позициями сетки, а не пикселями, идея состоит в том, что вы можете без проблем конвертировать позицию сетки в пиксели.

Моя сетка довольно проста. Верхний левый (0, 0). Итак, ваша ячейка 0 находится в (0, 0), поле 7 находится в (1, 6) и т.д.

Если у вас есть maxrows строки, то первые maxrows-1 элементы идут в (0, 0), (0, 1) и т.д. И элемент maxrows идет в (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_WIDTH и добавив смещение. Сделайте то же самое для ypos и BOX_HEIGHT.

Ответ 3

Я заметил, что это повторяющийся шаблон в макете: Начиная с 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: У меня на самом деле не было времени проверить это в браузере, чтобы могли быть некоторые ошибки:)