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