Как сделать гексагональную сетку на чистом CSS

  В новом сайте АБИТУРИЕНТ-2018 Высшей школы экономики и управления захотелось подчеркнуть единство, комплексность, взаимосвязанность образовательных направлений, расположив ассоциирующие миниатюры в виде сот пчелиного улья. Задачка оказалась совсем не тривиальной, однако, применённая метода может служить решением довольно широкого круга задач визуализации. Здесь результаты моих упражнений при создании адаптивной гексагональной сетки. Кроме того, подробно объяснены несколько CSS-селекторов и методов сокращения кода, а так же некоторые трюки для вычисления плавающего значения свойства height элементов CSS.

Создадим неупорядоченный список с ID #grid, который определяет размеры шестигранной сетки. Я использовал ширину в 60% от базового элемента. Сетка будет расположены в центре страницы, для чего используется margin: 0 auto; при вычислении левого и правого отступов. Можно изменять эти значения по своему усмотрению, что бы гексагональная сетка расположилась соответствующим образом относительно базового элемента.

<body>
    <ul id="grid" class="clear"></ul>
</body>
#grid {
    position: relative;
    width: 60%;
    margin: 0 auto;
    padding: 0; /* Clears unordered list default */
}

.clear:after {
    content: "";
    display: block;
    clear: both;
}

В неупорядоченный список для создания сетки будем добавлять блоковые элементы сторона к стороне, side-by-side. Что бы предотвратить обтекание, определяя позиционирование этих элементов, как float, используем clearfix. Свойство float не влияет на высоту и ширину контейнера элемента, но контент вокруг float-объекта будет его обтекать, а clearfix предотвратит это.

КАК СОЗДАТЬ ШЕСТИУГОЛЬНИК, ИСПОЛЬЗУЯ ТОЛЬКО CSS

Есть несколько решений для создания шестиугольника на чистом CSS. Здесь использован довольно удачный метод, предложенный в статье How To Create Pure CSS Hexagonal Grids, совместимый с большинством старых браузеров и реализованный с учётом требований адаптивного дизайна.

Для начало сделаем простой параллелограмм и применим к нему 2D трансформацию, в дальнейшем используя свойство overflow для маскирования лишнего при отрисовке шестиугольника.

Начнём с добавления тега <li> как элемента неупорядоченного списка #grid, который станет параллелограмом для дальнейшего маскирования лишнего:

<body>
    <ul id="grid" class="clear">
        <li></li>
    </ul>
</body>

Повернём параллелограмм  <li>, используя следующий код CSS:

#grid li {
	list-style-type: none;
	position: relative;
	float: left;
	width: 27.85714285714286%;
	padding: 0 0 32.16760145166612% 0;
	-o-transform: rotate(-60deg) skewY(30deg);
	-moz-transform: rotate(-60deg) skewY(30deg);
	-webkit-transform: rotate(-60deg) skewY(30deg);
	-ms-transform: rotate(-60deg) skewY(30deg);
	transform: rotate(-60deg) skewY(30deg);
	background: #fd005f;
}
  • list-style-type: none; — удаляет маркер перед элементом списка.
  • отступ, padding, нижней границы на высоту элемента — это обычная уловка, используемая в адаптивном дизайн для вычисления плавающей высоты в зависимости от ширины контейнера элемента.
  • -o-, -moz-, -webkit-, -ms- — свойство transform объявлено 5 раз, в 4-х из которых используются указанные выше приставки. Эти браузерные префиксы, которые необходимы для работы новых свойств CSS на браузерах устаревших. Настоятельно рекомендую использовать www.caniuse.com для того, что бы убедиться в совместимости свойств или селекторов CSS в совместимости со старыми браузерами и понять неоходимость использования префиксов.

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

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

Добавим слой  <div> класса hexagon в качестве дочернего элемента <li>:

<body>
    <ul id="grid" class="clear">
        <li>
            <div class="hexagon"></div>
        </li>
    </ul>
</body>

Опишем класс hexagon:

#grid li .hexagon {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: #fdbf00;
	-o-transform: skewY(-30deg) rotate(60deg);
	-moz-transform: skewY(-30deg) rotate(60deg);
	-webkit-transform: skewY(-30deg) rotate(60deg);
	-ms-transform: skewY(-30deg) rotate(60deg);
	transform: skewY(-30deg) rotate(60deg);
}
  • position: absolute; top: 0; left: 0; — Значение высоты элемента <li> равно 0, здесь использован трюк адаптивного дизайна для вычисления высоты, не установки статического значения. Это прерывает отображение всех дочерних элементов <li>, которые позиционируются как относительные. Чтобы этого не произошло с нашим <div>, мы устанавливаем его свойство position: absolute, тем самым исключаем шестиугольник из потока макета страницы. Таким образом, на него не повлияет значение высоты 0 родителя. Свойства top: 0; left: 0; определяют его положение, как абсолютное в родительском контейнере.
  • Для фактической формы устанавливается высота и ширина в 100% от родителя. Это гарантирует одинаковую высоту и ширину элемента <li> до применения преобразований. 2D-преобразования, примененные к элементу <li>, также будут применяться к его дочерним элементам; сделаем так, чтобы конечная форма не поворачивалась и не искажалась при трансформации.

Теперь у нас должно получиться что-то, похожее на это:

Ну, совсем не шестиугольник, не спешите, скоро будет нам счастье!

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

Свойство overflow может применяться для определения правил отображения при наложении контейнеров элементов друг на друга. Существует несколько возможных значений для overflow:

  • visible — наложение не отсекается и отображает вне элемента. Это значение по умолчанию.
  • hidden — наложение отсекается и не будет видно.
  • scroll — наложение обрезается и для просмотра добавляется полоса прокрутки наложенного содержимого. Scroll-бар будет добавлен, независимо от того, есть наложение или нет.
  • auto — наложение обрезается и полоса прокрутки добавляется для просмотра наложения. Полосы прокрутки добавляются только если есть наложение.
  • initial — наложение обрезается и полоса прокрутки добавляется для просмотра наложения.
    В полосы прокрутки добавляется только если нет перелива содержимого.
  • inherit — устанавливает знаение свойства в значение по умолчанию. Наследует значение этого свойства от родительского элемента.

Сейчас, конечная фигура имеет свойство overflow контейнера родительского элемента <li>. Мы добавим (overflow: hidden; к элементу <li> для того, чтобы скрыть его при наложении другого контейнера.

#grid li {
    list-style-type: none;
    position: relative;
    float: left;
    width: 27.85714285714286%;
    padding: 0 0 32.16760145166612% 0;
    -o-transform: rotate(-60deg) skewY(30deg);
    -moz-transform: rotate(-60deg) skewY(30deg);
    -webkit-transform: rotate(-60deg) skewY(30deg);
    -ms-transform: rotate(-60deg) skewY(30deg);
    transform: rotate(-60deg) skewY(30deg);
    background: fd005f;
    overflow: hidden;
}

Теперь, окончательная форма обрезана по краям своего родительского элемента и должна быть похожа на то, что изображено ниже.

Осталось только скрыть ненужное <li> элемента, так что бы остался шестиугольник.

Для этого добавим свойство visible со значением hidden;, чтобы наш элемент <li> стал невидимым. Мы также удалим свойство background — нам не нужен фон невидимого элемента.

#grid li {
    list-style-type: none;
    position: relative;
    float: left;
    width: 27.85714285714286%;
    padding: 0 0 32.16760145166612% 0;
    -o-transform: rotate(-60deg) skewY(30deg);
    -moz-transform: rotate(-60deg) skewY(30deg);
    -webkit-transform: rotate(-60deg) skewY(30deg);
    -ms-transform: rotate(-60deg) skewY(30deg);
    transform: rotate(-60deg) skewY(30deg);
    overflow: hidden;
    visibility: hidden;
}

Если элемент <li> стал не видимым, то его дочерние элементы унаследовали свойства видимости и мы ольше никогда не увидим шестиугольник.

Поэтому, решим эту проблему путем добавления visible в классе hexagon со значением visible, то опять сделает шестиугольник видимым.

Но вместо этого давайте создадим новый селектор:

#grid li * {
    visibility: visible;
}

Селектор дикой карты * — выбирает все дочерние элементы, содержащиеся в <li> и применяет visible влыдельца с указанного значения ко всем элементам: visible. Используя подстановочный селектор нам не надо повторно объявлять видимость для каждого дочернего элемента. Не создавать внутри каждого <li> дополнительного объявления для получения шестиугольника.

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

Наконец мы добавим overflow к классу hexagon для любых дочерних элементов. Например, если добавить в элемент изображение, то он будет обрезан по форме шестиугольника, а не будет перекрывать контейнер.

#grid li .hexagon {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #fdbf00;
    -o-transform: skewY(-30deg) rotate(60deg);
    -moz-transform: skewY(-30deg) rotate(60deg);
    -webkit-transform: skewY(-30deg) rotate(60deg);
    -ms-transform: skewY(-30deg) rotate(60deg);
    transform: skewY(-30deg) rotate(60deg);
    overflow: hidden;
}

Поэкспериментирйте с этой техникой!

Этот метод не ограничивается только шестиугольниками, он применим к другим фигурам для использования качестве масок с перекрытием. Посмотрите предыдущую статью «Рецепты CSS: геометрические фигуры».

Пожалуйста, не стесняйтесь поделиться своим опытом в комментариях, хотелось бы познакомится с вашей техникой.

СОЗДАНИЕ ГИБКОЙ СЕТКИ ИЗ шестиугольникОВ

Мы собираемся использовать массу шестиугольников для создания сетки!

Добавить еще несколько элементов <li>, содержащие элемент <div> с классом hexagon, в неупорядоченный список #grid;.

<body>
    <ul id="grid" class="clear">
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
        <li>
            <div class="hexagon"></div>
        </li>
    </ul>
</body>

Теперь у нас в общей сложности 12 шестиугольников.

По сути, это сетка из шестиугольников, она является гибкой и функциональной, но визуально не очень привлекательна. Адаптивная сота сетки будет иметь куда большую привлекательность, если будет напоминать пчелиный улей и, то же время, можно будет добавлять или уменьшать количество шестиугольников.

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

Для достижения этой цели мы могли бы добавить класс второго, пятого, восьмого и одиннадцатого шестиугольника с описанием отступов, но это потребует слишком много рутинной работы, особенно если мы планируем в будущем добавлять больше шестиугольников в соту.

К счастью, в CSS есть очень полезный селектор, который называется nth‑child; этот селектор позволит нам легко добавлять поля к центральной колонне из шестиугольников, а также позволит распространить правило на столько угодно шестиугольников без добавления классов для каждого нового.

Начнём с добавления в CSS селектора и стилей:

#grid li:nth&#8209;child(2) {
  margin: 0 1%;
}

#grid li:nth‑child(2) — это выбор второго  <li> из дочерних элементов в неупорядоченном списке элементов с ID #grid.

Как видите, теперь второй плавающий шестиугольник имеет отступ слева и справа в 1%.

Теперь, мы можем создать селекторы :nth‑child(5), :nth‑child(8), :nth‑child(11) подобно второму шестиугольнику каждой строки в сетке, но это слишком монотонная работа, которую следует избегать.

Вместо этого мы используем формулу (an+b), где a представляет собой параметр цикла, n — счетчик, а b — смещение. Счетчик n начинается с 0 и увеличивается на 1 на каждом цикле.

Вот пример использования (5n+7):

Цикл 1: (5 х 0) + 7 = 7

Цикл 2: (5 х 1) + 7 = 12

Цикл 3: (5 х 2) + 7 = 17

Цикл 4: (5 х 3) + 7 = 22

и т. д.

Цикл начинается со смещения 7 и с шагом 5 каждого цикла.

Мы можем использовать формулу (3n+2) для выбора второго шестиугольника в каждой строке сетки.

Цикл 1: (3 х 0) + 2 = 2

Цикл 2: (3 х 1) + 2 = 5

Цикл 3: (3 х 2) + 2 = 8

4 цикл: (3 х 3) + 2 = 11

и т. д.

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

Заменим (2) в селекторе на формулу (3n+2).

#grid li:nth&#8209;child(3n+2) {
  margin: 0 1%;
}

Сейчас каждый второй шестиугольник в каждой строки сетке имеет плавающие отступы слева и справа величиной в 1%.

С помощью селектора nth‑child можно сместить каждый ряд вправо, чтобы достичь эффекта соты, к которму мы стремитесь. В то же время мы добавим верхней и нижней отступы по вертикали между строками.

#grid li:nth&#8209;child(6n+4), #grid li:nth&#8209;child(6n+5), #grid li:nth&#8209;child(6n+6) {
    margin-top: -6.9285714285%;
    margin-bottom: -6.9285714285%;
    -o-transform: translateX(50%) rotate(-60deg) skewY(30deg);
    -moz-transform: translateX(50%) rotate(-60deg) skewY(30deg);
    -webkit-transform: translateX(50%) rotate(-60deg) skewY(30deg);
    -ms-transform: translateX(50%) rotate(-60deg) skewY(30deg);
    transform: translateX(50%) rotate(-60deg) skewY(30deg);
}

Формулы, используемые в наших селекторах nth‑child: (6n+4), (6n+5) и (6n+6).

(6n+4)

цикл 1: (6 х 0) + 4 = 4

Цикл 2: (6 х 1) + 4 = 10

Цикл 3: (6 х 2) + 4 = 16

Цикл 4: (6 х 3) + 4 = 22

и т. д.

(6n+5)

цикл 1: (6 х 0) + 5 = 5

Цикл 2: (6 х 1) + 5 = 11

Цикл 3: (6 х 2) + 5 = 17

Цикл 4: (6 х 3) + 5 = 23

и т. д.

(6n+6)

цикл 1: (6 х 0) + 6 = 6

Цикл 2: (6 х 1) + 6 = 12

Цикл 3: (6 х 2) + 6 = 18

Цикл 4: (6 х 3) + 6 = 24

и т. д.

В translateX добавлено значение смещениея по оси Х на 50%, значения поворота и наклона элементов.
Это связано с каскадностью правил CSS только при объявлении значения свойства Transform translateX, которое будет переопределять ранее объявленное при создании параллелограмма правило, для поворота и наклона выбранного элемента  <li>.

Добавим ещё одно, последнее, правило, которое выбирает первый шестиугольник из каждой второй строки и слегка подталкивает целый ряд вправо, оставляя одинаковыми просветы вокруг каждого шестиугольника. Это может быть достигнуто путем увеличения translateX на значение 0.5% в предыдущем правиле CSS, однако, используя перерасчет десятичного значения может иметь очень нежелательные последствия в некоторых браузерах.

Вместо этого, мы используем селектор nth‑child, что бы снова применить его к левому краю для нужной нам настройки.

#grid li:nth&#8209;child(6n+4) { 
    margin-left: 0.5%;
}

С очень небольшим количеством кода и повторений, благодаря CSS-селектору nth‑child, мы полностью решили задачу создания гексагональной адаптивной сетки с плавающими значениями высоты и ширины.

Буду рад видеть ваши творения, использующие этот метод. Не стесняйтесь разместить ссылку в комментариях!

CC BY-NC 4.0 Как сделать гексагональную сетку на чистом CSS, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.


2 нравится это

Добавить комментарий