Макет кладки только на CSS

134

Мне нужно реализовать довольно нестандартную планировку кладки. Однако по ряду причин я не хочу использовать для этого JavaScript.

Сетка из нескольких столбцов прямоугольников разной высоты.

Параметры:

  • Все элементы имеют одинаковую ширину
  • Элементы имеют высоту, которую невозможно вычислить на стороне сервера (изображение плюс различное количество текста)
  • Я могу жить с фиксированным количеством столбцов, если мне нужно

есть тривиальное решение, которое работает в современных браузерах - column-countсвойство.

Проблема с этим решением в том, что элементы упорядочены по столбцам:

Начиная с самого верхнего левого поля, они пронумерованы от 1 до 4 прямо вниз, в самом верхнем поле следующего столбца - 5 и т. Д.

Пока мне нужно, чтобы элементы были упорядочены по строкам, хотя бы примерно:

Начиная с самого верхнего левого прямоугольника, они пронумерованы от 1 до 6, но поскольку прямоугольник 5 является самым коротким, прямоугольник под ним - 7, так как он выглядит как находящийся в строке выше, чем следующий крайний левый прямоугольник.

Подходы, которые я пробовал, не работают:

Теперь я мог бы изменить рендеринг на стороне сервера и переупорядочить элементы, разделив количество элементов на количество столбцов, но это сложно и подвержено ошибкам (в зависимости от того, как браузеры решают разделить список элементов на столбцы), поэтому я хотел бы по возможности избегать этого.

Есть ли какая-то новомодная магия flexbox, которая делает это возможным?

Пекка
источник
7
Не могу придумать способ, который бы не зависел от заранее заданных высот. Если вы пересмотрите JS, взгляните на stackoverflow.com/questions/13518653/…, где я реализую такое решение, которое довольно просто.
Габриэле Петриоли
1
@Gaby реализует ваше решение прямо сейчас, спасибо!
Pekka
4
Я понимаю, что вы сказали только CSS. Я просто хочу упомянуть, что Masonry больше не требует jQuery - минимизированная библиотека составляет менее 8 КБ - и может быть инициализирована только с помощью html. Просто для справки jsfiddle.net/wp7kuk1t
I haz kode
1
Если вы можете заранее определить высоту элементов, зная высоту строки, размер шрифта (вам нужно будет указать конкретный шрифт и произвести некоторые умные вычисления), высоту изображения, поле по вертикали и отступы, вы можете сделай это. В противном случае вы не сможете сделать это, используя только CSS. Вы также можете использовать что-то вроде PhantomJS для предварительного рендеринга каждого элемента и получения высоты этого элемента, но при этом будут добавлены значительные накладные расходы / задержка.
Timothy Zorn

Ответы:

157

Flexbox

Динамическая разметка каменной кладки невозможна с помощью flexbox, по крайней мере, в чистом и эффективном виде.

Flexbox - это система одномерного макета. Это означает, что он может выравнивать элементы по горизонтальным ИЛИ вертикальным линиям. Гибкий элемент ограничен своей строкой или столбцом.

Настоящая сетка двумерна, то есть она может выравнивать элементы по горизонтальным И вертикальным линиям. Элементы содержимого могут одновременно охватывать строки и столбцы, чего не могут сделать гибкие элементы.

Вот почему возможности flexbox для построения сеток ограничены. Это также причина, по которой W3C разработал другую технологию CSS3, Grid Layout .


row wrap

В гибком контейнере с flex-flow: row wrapгибкими элементами должны быть перенесены в новые строки .

Это означает, что гибкий элемент не может быть перенесен под другой элемент в той же строке .

Обратите внимание на то, как div # 3 оборачивается под div # 1 , создавая новую строку. Он не может быть заключен в div # 2 .

В результате, когда элементы не самые высокие в ряду, остается пустое пространство, создавая неприглядные зазоры.


column wrap

Если вы переключитесь на flex-flow: column wrap, сетка станет более доступной. Однако контейнер с направлением столбца сразу же имеет четыре потенциальных проблемы:

  1. Элементы Flex перемещаются вертикально, а не горизонтально (как вам нужно в этом случае).
  2. Контейнер расширяется по горизонтали, а не по вертикали (как макет Pinterest).
  3. Для этого требуется, чтобы контейнер имел фиксированную высоту, чтобы предметы знали, где их оборачивать.
  4. На момент написания этой статьи он имеет недостаток во всех основных браузерах, в которых контейнер не расширяется для размещения дополнительных столбцов .

В результате контейнер в направлении столбца не подходит в этом случае, как и во многих других случаях.


CSS Grid с неопределенными размерами элементов

Макет сетки был бы идеальным решением вашей проблемы, если бы можно было заранее определить различную высоту элементов контента . Все остальные требования находятся в пределах возможностей Grid.

Необходимо знать ширину и высоту элементов сетки, чтобы закрыть пробелы с окружающими элементами.

Таким образом, Grid, который является лучшим CSS, который может предложить для построения горизонтального макета кирпичной кладки, в этом случае не справляется.

Фактически, пока не появится технология CSS, способная автоматически закрывать пробелы, у CSS вообще нет решения. Что-то вроде этого, вероятно, потребует перекомпоновки документа, поэтому я не уверен, насколько это будет полезно или эффективно.

Вам понадобится сценарий.

Решения JavaScript, как правило, используют абсолютное позиционирование, которое удаляет элементы контента из потока документов, чтобы переупорядочить их без пропусков. Вот два примера:


CSS Grid с определенными размерами элементов

Для макетов, в которых известны ширина и высота элементов содержимого, вот горизонтальный макет кладки на чистом CSS:

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}
<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

демонстрация jsFiddle


Как это устроено

  1. Создайте контейнер сетки на уровне блоков. ( inline-gridбыл бы другой вариант)
  2. grid-auto-rowsСвойство задает высоту автоматически генерируемых строк. В этой сетке каждая строка имеет высоту 50 пикселей.
  3. grid-gapСвойство является обобщающим для grid-column-gapи grid-row-gap. Это правило устанавливает промежуток в 10 пикселей между элементами сетки. (Это не относится к области между предметами и контейнером.)
  4. grid-template-columnsСвойство устанавливает ширину явно определенных столбцов.

    repeatОбозначения определяет образец повторяющихся столбцов (или строк).

    auto-fillФункция сообщает сетку выстраиваться столько столбцов (или строк) , как это возможно без переполнения контейнера. (Это может создать поведение, подобное гибкому макету flex-wrap: wrap.)

    minmax()Функция устанавливает минимальный и максимальный диапазон размеров для каждого столбца (или строки). В приведенном выше коде ширина каждого столбца будет составлять минимум 30% контейнера и максимум доступного свободного места.

    frБлок представляет собой долю свободного пространства в контейнере сетки. Это сопоставимо со flex-growсвойством flexbox .

  5. С помощью grid-rowи spanмы сообщаем элементам сетки, сколько строк они должны занимать.


Поддержка браузером CSS Grid

  • Chrome - полная поддержка с 8 марта 2017 г. (версия 57)
  • Firefox - полная поддержка с 6 марта 2017 г. (версия 52)
  • Safari - полная поддержка с 26 марта 2017 г. (версия 10.1)
  • Edge - полная поддержка с 16 октября 2017 г. (версия 16)
  • IE11 - нет поддержки текущей спецификации; поддерживает устаревшую версию

Вот полная картина: http://caniuse.com/#search=grid


Классная функция наложения сетки в Firefox

В инструментах разработчика Firefox, когда вы проверяете контейнер сетки, в объявлении CSS есть крошечный значок сетки. При щелчке он отображает контур вашей сетки на странице.

Подробнее здесь: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

Майкл Бенджамин
источник
5
Фантастический ответ! Можно ли с помощью решения «Сетка CSS с определенными размерами элементов» выразить высоту ячейки в процентах от ширины ячейки? Это было бы полезно для отображения изображений с известным соотношением сторон. Мы хотим постоянно поддерживать соотношение сторон.
Оливер Джозеф Эш
Спасибо. Что касается проблемы с соотношением сторон изображений, то метод «процентной ширины ячейки» (трюк с процентным заполнением?), Если на то пошло, ненадежен в Grid или Flexbox. Смотрите здесь и здесь . @OliverJosephAsh
Майкл Бенджамин
Да, я имею в виду трюк с процентным заполнением. Можно ли использовать какие-либо обходные пути в сочетании с вашим решением макета кладки? Для контекста мы хотим реализовать макет кладки только CSS для изображений на unsplash.com .
Оливер Джозеф Эш
1
К сожалению, использование JS для этого не вариант. Мы хотим включить рендеринг на стороне сервера из соображений производительности и SEO. Это означает, что макет должен быть отрисован до того, как клиентский JavaScript будет загружен, проанализирован и выполнен. Учитывая, что это кажется невозможным, я думаю, нам придется пойти на компромисс где-нибудь в процессе! Спасибо за вашу помощь :-)
Оливер Джозеф Эш
1
Обновление спецификации: в flexbox процентные поля и отступы теперь снова настроены на разрешение встроенного размера контейнера. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Майкл Бенджамин
13

Это недавно открытый метод с использованием flexbox: https://tobiasahlin.com/blog/masonry-with-css/ .

Эта статья имеет для меня смысл, но я не пробовал ее использовать, поэтому не знаю, есть ли какие-либо предостережения, кроме упомянутых в ответе Майкла.

Вот образец из статьи, в которой используется orderсвойство в сочетании с :nth-child.

Фрагмент стека

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 960px;
  
  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }
<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  
  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>

Оливер Джозеф Эш
источник
Во-первых, ответы только по ссылке - это плохие ответы, например, когда такая ссылка умирает, умирает и ответ. Во-вторых, если вы прочитаете данный ответ более внимательно, вы обнаружите, что то, что упомянуто в вашей ссылке, покрыто. Проще говоря, использование одного только Flexbox имеет слишком много ограничений, если только то, что упомянуто выше, не является проблемой.
Асон
3
Я очень внимательно прочитал принятый ответ - вы даже увидите некоторые комментарии, которые я добавил в его нижнюю часть. Подход flexbox, с которым я связался, уникален и не охвачен этим ответом. Я не хочу повторять статью, потому что она очень сложная, и статья отлично справляется с этим.
Оливер Джозеф Эш
Единственное, что делает связка по-другому, - это использование orderсвойства, что делает его похожим на движение элементов слева направо. Тем не менее, его главная хитрость в том flex-flow: column wrap, что упоминается в ответе выше. Кроме того, он не может перемещать элементы так, как это делает настоящая кладка, например, если 3-й и 4-й элементы поместятся в 3-м столбце, они будут, а связанный - нет. Но опять же, как я уже сказал, все зависит от требований, и в этом случае (в этом вопросе) это не сработает так, как просили.
Асон
Более того, речь не идет о том, что «вы не хотите повторять статью» , ответ должен содержать существенную часть кода, чтобы он все еще был действителен, когда связанный ресурс умирает.
Асон
3
Я обновил его, добавил образец из статьи + голос за.
Асон