Розкладка кладки лише для CSS


134

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

Сітка з декількох стовпців прямокутників різної висоти.

Параметри:

  • Всі елементи мають однакову ширину
  • Елементи мають висоту, яку неможливо обчислити стороною сервера (зображення плюс різноманітна кількість тексту)
  • Я можу жити з фіксованою кількістю стовпців, якщо доведеться

є тривіальне рішення цієї проблеми, яке працює в сучасних браузерах, в column-countвласності.

Проблема з цим рішенням полягає в тому, що елементи впорядковуються у стовпцях:

Починаючи з верхнього лівого вікна, вони пронумеровані від 1 до 4 прямо вниз, у верхньому верхньому полі в наступному стовпці 5 і так далі.

Хоча мені потрібні елементи впорядкувати в рядках, принаймні приблизно:

Починаючи з верхнього лівого вікна, вони пронумеровані від 1 до 6 прямо навпроти, але оскільки вікно 5 найкоротше, це поле під ним 7, оскільки воно має вигляд на рядок вище, ніж наступне поле в крайній лівій частині.

Підходи, які я намагався не спрацювати:

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

Чи є якась новомодна магія флексокса, яка робить це можливим?


7
Не можу придумати спосіб, який не залежить від визначених висот. Якщо ви переглядаєте JS, подивіться на stackoverflow.com/questions/13518653/…, де я реалізую таке рішення, яке досить просте.
Габріеле Петріолі

1
@Gaby зараз реалізуючи ваше рішення, дякую!
Pekka

4
Я розумію, що ви сказали лише CSS. Я просто хочу зазначити, що Masonry більше не вимагає jQuery - мінімізована бібліотека не перевищує 8 кбіт - і може бути ініціалізована лише html. Тільки для довідки jsfiddle.net/wp7kuk1t
я

1
Якщо ви можете заздалегідь визначити висоту елементів, знаючи висоту рядка, розмір шрифту (вам доведеться обслуговувати певний шрифт і робити кілька розумних обчислень), висоту зображення, запас вершини та прокладку, ви можете зробити це. В іншому випадку ви не можете зробити це, використовуючи лише CSS. Ви також можете використати щось на зразок PhantomJS для попереднього відображення кожного елемента та отримання висоти цього елемента, але додані значні накладні витрати / затримки.
Тимофій Зорн

Відповіді:


157

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. Елементи гнучкості течуть вертикально, а не горизонтально (як вам потрібно в цьому випадку).
  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Блок являє собою частку вільного простору в контейнері сітки. Це можна порівняти з властивістю flexbox flex-grow.

  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 для зображень на сайті nonplash.com .
Олівер Джозеф Еш

1
На жаль, використання JS для цього не є можливим. Ми хочемо включити візуалізацію на стороні сервера з міркувань продуктивності та SEO. Це означає, що макет повинен бути наданий перед завантаженням, розбором та виконанням JavaScript на стороні клієнта. Зважаючи на це, здається, це неможливо, я думаю, нам доведеться робити торги десь уздовж лінії! Дякую за допомогу :-)
Олівер Джозеф Еш

1
Оновлення специфікацій: у flexbox тепер встановлюються відсоткові поля та прокладки, щоб знову вирішити вбудований розмір контейнера. drafts.csswg.org/css-flexbox/#item-margins @OliverJosephAsh
Майкл Бенджамін

13

Це нещодавно відкрита техніка, що стосується флексокса: 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
Я дуже ретельно прочитав прийняту відповідь - ви навіть побачите деякі коментарі, які я додав до нижньої частини. Підхід до флексбоксу, з яким я пов'язаний, є унікальним і не охоплений цією відповіддю. Я не хочу повторювати статтю, тому що вона дуже складна, і стаття робить велику роботу для висвітлення цього.
Олівер Джозеф Еш,

Єдине, що пов'язаний робить інше - це використовувати orderвластивість, роблячи його схожим на те, що елементи перетікають зліва направо. І все-таки головна її хитрість - це те flex-flow: column wrap, про що йдеться у наведеній вище відповіді. Крім того, він не може обробляти предмети так, як це робить справжня кладка, наприклад, якщо 3-й і 4-й предмет помістилися б у 3-му стовпчику, вони б, пов'язаний - ні. Але знову ж таки, як я вже сказав, все залежить від того, які вимоги є, і в цьому випадку (це питання) він не працюватиме так, як вимагали.
Асон

Крім того, мова йде не про те, "ви не хочете повторювати статтю" , відповідь повинна містити істотну частину коду, тому він все одно буде дійсним, коли пов'язаний ресурс відмирає.
Асон

3
Я оновив його, додав зразок із статті + резюме.
Асон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.