diff --git a/people/alextaraskina/index.md b/people/alextaraskina/index.md index 7a2020ba8d..4e236a2f38 100644 --- a/people/alextaraskina/index.md +++ b/people/alextaraskina/index.md @@ -1,6 +1,7 @@ --- name: 'Саша Тараскина' url: https://github.com/alexTaraskina +photo: photo.jpg badges: - first-contribution-small --- diff --git a/people/alextaraskina/photo.jpg b/people/alextaraskina/photo.jpg new file mode 100644 index 0000000000..09dfce47b9 Binary files /dev/null and b/people/alextaraskina/photo.jpg differ diff --git a/recipes/index.md b/recipes/index.md index 28df10a51c..0d5afe72da 100644 --- a/recipes/index.md +++ b/recipes/index.md @@ -14,6 +14,7 @@ groups: - container - lazy-load-with-preview - validation + - masonry - name: 'Работа с GitHub' items: - github-new-profile diff --git a/recipes/masonry/demos/exception/index.html b/recipes/masonry/demos/exception/index.html new file mode 100644 index 0000000000..a9a412d725 --- /dev/null +++ b/recipes/masonry/demos/exception/index.html @@ -0,0 +1,97 @@ + + + + Исключение — Плиточная раскладка — Дока + + + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + diff --git a/recipes/masonry/demos/five-columns/index.html b/recipes/masonry/demos/five-columns/index.html new file mode 100644 index 0000000000..fb24cafb35 --- /dev/null +++ b/recipes/masonry/demos/five-columns/index.html @@ -0,0 +1,110 @@ + + + + Пять колонок — Плиточная раскладка — Дока + + + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+ +
+
+
+
+
+ + diff --git a/recipes/masonry/demos/masonry-column/index.html b/recipes/masonry/demos/masonry-column/index.html new file mode 100644 index 0000000000..d37c934336 --- /dev/null +++ b/recipes/masonry/demos/masonry-column/index.html @@ -0,0 +1,167 @@ + + + + Раскладка на флексах с flex-direction: column — Плиточная раскладка — Дока + + + + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + + diff --git a/recipes/masonry/demos/masonry-row/index.html b/recipes/masonry/demos/masonry-row/index.html new file mode 100644 index 0000000000..9f7e64502a --- /dev/null +++ b/recipes/masonry/demos/masonry-row/index.html @@ -0,0 +1,167 @@ + + + + Раскладка на флексах с flex-direction: row — Плиточная раскладка — Дока + + + + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + + diff --git a/recipes/masonry/demos/order/index.html b/recipes/masonry/demos/order/index.html new file mode 100644 index 0000000000..c29d9f6e44 --- /dev/null +++ b/recipes/masonry/demos/order/index.html @@ -0,0 +1,66 @@ + + + + Порядок элементов — Плиточная раскладка — Дока + + + + + + + + + + +
+ 1 + 4 + 7 + 2 + 5 + 8 + 3 + 6 + 9 +
+ + + diff --git a/recipes/masonry/demos/with-column-break/index.html b/recipes/masonry/demos/with-column-break/index.html new file mode 100644 index 0000000000..655bcb478a --- /dev/null +++ b/recipes/masonry/demos/with-column-break/index.html @@ -0,0 +1,99 @@ + + + + Скрытые колонки-разделители — Плиточная раскладка — Дока + + + + + + + + + +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+ + diff --git a/recipes/masonry/index.md b/recipes/masonry/index.md new file mode 100644 index 0000000000..230d1fcc26 --- /dev/null +++ b/recipes/masonry/index.md @@ -0,0 +1,178 @@ +--- +title: "Плиточная раскладка" +description: "Как реализовать плиточную раскладку на чистом CSS во времена со слабой поддержкой grid-template-rows: masonry." +authors: + - alextaraskina +keywords: + - masonry + - masonry layout +related: + - css/grid-guide + - css/flex + - css/child +tags: + - article +--- + +## Задача + +Плиточная раскладка — это метод компоновки, при котором блоки по одной из осей вписываются в стандартную сетку (занимают определённое количество колонок или строк), а по другой оси располагаются так, чтобы не оставалось лишнего пространства, как бы прижимаются вплотную друг к другу. + + + +В 2020 году был опубликован черновик спецификации [CSS Grid Level 3](https://drafts.csswg.org/css-grid-3/). В этом документе описывается простой способ создания плиточной раскладки. Для реализации необходимо создать контейнер с блоками и задать одной из осей контейнера свойство [`grid-template-*`](/css/grid-template/) со значением `masonry`. Выглядит это следующим образом: + +```css +.container { + display: grid; + grid-template-rows: masonry; + grid-template-columns: repeat(3, 1fr); +} +``` + +В данный момент эта технология экспериментальная и пока доступна только в браузере [Firefox Nightly](https://www.mozilla.org/ru/firefox/channel/desktop/). Nightly — это нестабильная версия для активных пользователей, которые хотят участвовать в тестировании новых функций. + +Существует решение этой задачи на чистом CSS, но у него есть свои нюансы. Давайте разбираться. + +## Готовое решение + +Разметка — контейнер с блоками разной высоты: + +```html +
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
+``` + +Стили: + +```css +.container { + display: flex; + flex-wrap: wrap; + flex-direction: column; + gap: 8px 4px; + /* высота контейнера фиксированная */ + /* должна быть больше любой из колонок */ + height: 600px; + width: 100%; +} + +.item { + width: calc(100% / 3); +} + +.item:nth-child(3n + 1) { order: 1; } +.item:nth-child(3n + 2) { order: 2; } +.item:nth-child(3n) { order: 3; } + +.container::before, +.container::after { + content: ""; + flex-basis: 100%; + width: 0; + order: 2; +} +``` + +## Разбор решения + +Создаём флекс-контейнер, внутрь кладём блоки разной высоты. Задаём свойство [`flex-wrap`](/css/flex-wrap/) со значением `wrap` — нам нужно, чтобы блоки располагались в несколько рядов. Теперь определим направление расположения блоков внутри контейнера. На этом моменте и появляются первые сложности. + +Если установить на контейнере свойство [`flex-direction`](/css/flex-direction/) со значением `row`, то блоки будут размещаться в строку. При этом высота строки будет равна высоте наибольшего блока. Таким образом, по вертикальной оси между соседними блоками будет оставаться пустое пространство. + + + +Если установить `flex-direction` со значением `column`, то нужно также указать фиксированную высоту контейнера (иначе блоки не будут переноситься в новую колонку). Для динамической подгрузки данных, когда количество блоков и необходимая высота контейнера неизвестны, такое решение не подойдёт. В этом случае стоит смотреть в сторону решения на JavaScript. + +Ещё один недостаток — нарушается удобный при чтении слева направо порядок элементов. Но это можно победить, переопределив порядок элементов. + + + +### Меняем порядок блоков + +Переопределим порядок блоков с помощью свойства [`order`](/css/order/). Блок с меньшим числовым значением встаёт перед блоком с бо̀льшим значением, вне зависимости от фактического расположения блоков в HTML-разметке. + +Сейчас, при чтении слева направо, пользователь видит блоки в следующем порядке: + + + +Первый элемент и каждый третий после него (`3n+1`) должны рендериться первыми. Устанавливаем для них `order: 1`. Затем должны идти элементы `3n+2`, и в последнюю очередь — элементы `3n`. Для выбора элементов по формуле воспользуемся псевдоклассом [`:nth-child`](/css/child/). + +```css +.item:nth-child(3n+1) { order: 1; } +.item:nth-child(3n+2) { order: 2; } +.item:nth-child(3n) { order: 3; } +``` + + + +### Добавляем колонки-разделители + +Казалось бы, порядок элементов исправлен. Но такой подход сработает не всегда. Предположим, у нас есть колонка с невысокими блоками. Они вписываются по высоте в контейнер, и ещё остаётся значительное пространство под ними. В таком случае блок, который должен рендериться в следующей колонке, может не перенестись и попасть не в свою колонку. + +В примере ниже белым цветом обозначены блоки, которые не перенеслись в новую колонку, а вписались по размеру в предыдущую. Элемент 2 отрендерился в первой колонке, хотя должен быть во второй. Элемент 3 — во второй вместо третьей. + + + +Нам нужно как-то контролировать переносы элементов в новые колонки. Для решения этой задачи создадим дополнительные скрытые колонки-разделители. Разместим эти скрытые колонки между основными. Установим высоту в 100% — так, чтобы другие элементы не сливались со скрытой колонкой в одну. + +```css +/* Создаём две скрытые колонки с помощью псевдоэлементов */ +.container::before, +.container::after { + content: ""; + flex-basis: 100%; + width: 0; + order: 2; +} +``` + + + +Готово! + +## А что, если колонок больше трёх? + +Если колонок больше, нам нужно адаптировать решение, а именно: + +- поправить ширину колонок; +- добавить дополнительные колонки-разделители и поправить их расположение. + +В случае с тремя колонками мы создавали две дополнительные скрытые колонки с помощью псевдоэлементов [`::before`](/css/before/) и [`::after`](/css/after/). При большем числе видимых колонок число скрытых также растёт, и псевдоэлементы уже не подходят. Используем обычный [`
`](/html/div/). К элементу добавляем два класса — `item` и `break`. Класс `item` используется как для видимых элементов, так и для скрытых. Класс `break` — только для скрытых. + +Разметка для скрытой колонки: + +```html +
+``` + +Стили: + +```css +.break { + flex-basis: 100%; + width: 0; + margin: 0; +} +``` + +Количество скрытых колонок-разделителей равно количеству колонок минус один. В разметке эти элементы должны располагаться после всех видимых элементов, в самом конце контейнера. Их фактическое расположение [определяется стилями](/recipes/masonry/#menyaem-poryadok-blokov). + +