CSS Grid и пользовательские фигуры, часть 2. Анализ данных PlatoBlockchain. Вертикальный поиск. Ай.

CSS-сетка и пользовательские фигуры, часть 2

Хорошо, так что в последний раз, когда мы регистрировалисьмы использовали CSS Grid и комбинировали их с CSS clip-path и mask методы создания сеток причудливых форм.

Вот лишь одна из фантастических сеток, которые мы сделали вместе:

Готовы ко второму туру? Мы все еще работаем с CSS Grid, clip-pathи mask, но к концу этой статьи мы получим различные способы расположения изображений в сетке, включая некоторые эффекты наведения, которые создают аутентичный интерактивный опыт просмотра изображений.

И угадай что? Мы используем та же разметка, которую мы использовали в прошлый раз. Вот это еще раз:

<div class="gallery">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <!-- as many times as we want -->
</div>

Как и в предыдущей статье, нам нужен только контейнер с изображениями внутри. Больше ничего!

Вложенная сетка изображений

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

Давайте попробуем вложить изображение в центр сетки:

Начнем с установки сетки 2✕2 для четырех изображений:

.gallery {
  --s: 200px; /* controls the image size */
  --g: 10px; /* controls the gap between images */

  display: grid;
  gap: var(--g);
  grid-template-columns: repeat(2, auto);
}
.gallery > img {
  width: var(--s);
  aspect-ratio: 1;
  object-fit: cover;
}

Пока ничего сложного. Следующий шаг — обрезать угол нашего изображения, чтобы освободить место для вложенного изображения. У меня уже есть подробная статья как срезать углы с помощью clip-path и mask. Вы также можете использовать мой онлайн-генератор чтобы получить CSS для маскировки углов.

Здесь нам нужно вырезать углы под углом, равным 90deg. Мы можем использовать то же самое техника конического градиента из этой статьи, чтобы сделать это:

.gallery > img {
   mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}
.gallery > img:nth-child(1) { --_a: 90deg; }
.gallery > img:nth-child(2) { --_a: 180deg; }
.gallery > img:nth-child(3) { --_a: 0deg; }
.gallery > img:nth-child(4) { --_a:-90deg; }

Мы могли бы использовать clip-path метод срезания углов из той же статьи, но здесь больше подойдет маскирование градиентами, поскольку у нас для всех изображений одинаковая конфигурация — все, что нам нужно, это поворот (определяемый переменной --_a) получить эффект, поэтому мы маскируем изнутри, а не снаружи.

CSS-сетка и пользовательские фигуры, часть 2

Теперь мы можем разместить вложенное изображение внутри замаскированного пространства. Во-первых, давайте удостоверимся, что у нас есть пятый элемент изображения в HTML:

<div class="gallery">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
</div>

Чтобы разместить его здесь, мы будем полагаться на старое доброе абсолютное позиционирование:

.gallery > img:nth-child(5) {
  position: absolute;
  inset: calc(50% - .5*var(--s));
  clip-path: inset(calc(var(--g) / 4));
}

Ассоциация inset Свойство позволяет нам разместить изображение в центре, используя одно объявление. Мы знаем размер изображения (определяется переменной --s), и мы знаем, что размер контейнера равен 100%. Мы делаем некоторые математические расчеты, и расстояние от каждого края должно быть равно (100% - var(--s))/2.

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

Вам может быть интересно, почему мы используем clip-path вообще здесь. Мы используем его с вложенным изображением, чтобы обеспечить постоянный зазор. Если бы мы удалили его, вы бы заметили, что у нас не одинаковый разрыв между всеми изображениями. Таким образом, мы немного вырезаем пятое изображение, чтобы обеспечить правильное расстояние вокруг него.

Полный код еще раз:

.gallery {
  --s: 200px; /* controls the image size */
  --g: 10px;  /* controls the gap between images */
  
  display: grid;
  gap: var(--g);
  grid-template-columns: repeat(2, auto);
  position: relative;
}

.gallery > img {
  width: var(--s);
  aspect-ratio: 1;
  object-fit: cover;
  mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}

.gallery > img:nth-child(1) {--_a: 90deg}
.gallery > img:nth-child(2) {--_a:180deg}
.gallery > img:nth-child(3) {--_a:  0deg}
.gallery > img:nth-child(4) {--_a:-90deg}
.gallery > img:nth-child(5) {
  position: absolute;
  inset: calc(50% - .5*var(--s));
  clip-path: inset(calc(var(--g) / 4));
}

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

Это правда, и мы получим следующее:

Нет mask, Не clip-path. Да, код легко понять, но есть небольшой недостаток: цвет рамки должен совпадать с цветом основного фона, чтобы иллюзия была идеальной. Этого небольшого недостатка мне достаточно, чтобы усложнить код в обмен на реальную прозрачность, независимую от фона. Я не говорю, что пограничный подход плох или неправилен. Я бы рекомендовал это в большинстве случаев, когда известна предыстория. Но мы здесь для того, чтобы исследовать что-то новое и, что самое важное, создавать компоненты, которые не зависят от окружающей среды.

На этот раз попробуем другую форму:

На этот раз мы сделали вложенное изображение кругом, а не квадратом. Это простая задача с border-radius Но нам нужно использовать круглый вырез для других изображений. Однако на этот раз мы будем полагаться на radial-gradient() вместо conic-gradient() чтобы получить этот красивый округлый вид.

.gallery > img {
  mask: 
    radial-gradient(farthest-side at var(--_a),
      #0000 calc(50% + var(--g)/2), #000 calc(51% + var(--g)/2));
}
.gallery > img:nth-child(1) { --_a: calc(100% + var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(2) { --_a: calc(0%   - var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(3) { --_a: calc(100% + var(--g)/2) calc(0%   - var(--g)/2); }
.gallery > img:nth-child(4) { --_a: calc(0%   - var(--g)/2) calc(0%   - var(--g)/2); }

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

Диаграмма, показывающая центральные значения для каждого квадранта сетки.
CSS-сетка и пользовательские фигуры, часть 2

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

Теперь, когда у нас есть макет, давайте поговорим об эффекте наведения. Если вы не заметили, крутой эффект при наведении увеличивает размер вложенного изображения и соответствующим образом настраивает все остальное. Увеличение размера — относительно простая задача, но обновить градиент сложнее, поскольку по умолчанию градиенты нельзя анимировать. Чтобы преодолеть это, я буду использовать font-size взломать, чтобы иметь возможность анимировать радиальный градиент.

Если вы проверите код градиента, вы увидите, что я добавляю 1em:

mask: 
    radial-gradient(farthest-side at var(--_a),
      #0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));

Известно, что em единицы измерения относятся к родительскому элементу font-size, поэтому изменение font-size .gallery также изменит вычисленное em значение — это трюк, который мы используем. Мы анимируем font-size от значения 0 заданному значению, и в результате градиент анимируется, увеличивая вырезанную часть в соответствии с размером вложенного изображения, которое становится больше.

Вот код, который выделяет части, участвующие в эффекте наведения:

.gallery {
  --s: 200px; /* controls the image size */
  --g: 10px; /* controls the gaps between images */

  font-size: 0; /* initially we have 1em = 0 */
  transition: .5s;
}
/* we increase the cut-out by 1em */
.gallery > img {
  mask: 
    radial-gradient(farthest-side at var(--_a),
      #0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));
}
/* we increase the size by 2em */
.gallery > img:nth-child(5) {
  width: calc(var(--s) + 2em);
}
/* on hover 1em = S/5 */
.gallery:hover {
  font-size: calc(var(--s) / 5);
}

Ассоциация font-size трюк полезен, если мы хотим анимировать градиенты или другие свойства, которые нельзя анимировать. Пользовательские свойства, определенные с помощью @property, могут решить такую ​​проблему, но поддержка для этого на момент написания все еще отсутствует.

Я обнаружил font-size трюк от @SelenIT2 пытаясь решить вызов в Твиттере.

Другая форма? Пойдем!

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

Круглая сетка изображений

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

В структуре сетки HTML и CSS нет ничего нового, поэтому давайте пропустим эту часть и вместо этого сосредоточимся на желаемой круглой форме и эффекте наведения.

Мы собираемся использовать clip-path и ее circle() функция — как вы уже догадались! — вырежьте из изображений круг.

Показаны два состояния изображения: естественное состояние слева и состояние при наведении справа, включая значения пути обрезки для их создания.
CSS-сетка и пользовательские фигуры, часть 2

Этот рисунок иллюстрирует clip-path используется для первого изображения. Слева показано исходное состояние изображения, а справа — состояние при наведении. Вы можете использовать этот онлайн-инструмент играть и визуализировать clip-path значения.

Для других изображений мы можем обновить центр круга (70% 70%), чтобы получить следующий код:

.gallery > img:hover {
  --_c: 50%; /* same as "50% at 50% 50%" */
}
.gallery > img:nth-child(1) {
  clip-path: circle(var(--_c, 55% at 70% 70%));
}
.gallery > img:nth-child(2) {
  clip-path: circle(var(--_c, 55% at 30% 70%));
}
.gallery > img:nth-child(3) {
  clip-path: circle(var(--_c, 55% at 70% 30%));
}
.gallery > img:nth-child(4) {
  clip-path: circle(var(--_c, 55% at 30% 30%));
}

Обратите внимание, как мы определяем clip-path значения как запасной вариант внутри var(). Этот способ позволяет нам более легко обновлять значение при наведении, устанавливая значение --_c переменная. Когда используешь circle(), положение центральной точки по умолчанию 50% 50%, поэтому мы можем опустить это для более краткого кода. Вот почему вы видите, что мы только устанавливаем 50% вместо 50% at 50% 50%.

Затем мы увеличиваем размер нашего изображения при наведении курсора мыши до общего размера сетки, чтобы мы могли покрыть другие изображения. Мы также обеспечиваем z-index имеет более высокое значение на изображении при наведении, поэтому оно находится вверху в нашем списке. контекст стекирования.

.gallery {
  --s: 200px; /* controls the image size */
  --g: 8px;   /* controls the gap between images */

  display: grid;
  grid: auto-flow var(--s) / repeat(2, var(--s));
  gap: var(--g);
}

.gallery > img {
  width: 100%; 
  aspect-ratio: 1;
  cursor: pointer;
  z-index: 0;
  transition: .25s, z-index 0s .25s;
}
.gallery > img:hover {
  --_c: 50%; /* change the center point on hover */
  width: calc(200% + var(--g));
  z-index: 1;
  transition: .4s, z-index 0s;
}

.gallery > img:nth-child(1){
  clip-path: circle(var(--_c, 55% at 70% 70%));
  place-self: start;
}
.gallery > img:nth-child(2){
  clip-path: circle(var(--_c, 55% at 30% 70%));
  place-self: start end;
}
.gallery > img:nth-child(3){
  clip-path: circle(var(--_c, 55% at 70% 30%));
  place-self: end start;
}
.gallery > img:nth-child(4){
  clip-path: circle(var(--_c, 55% at 30% 30%));
  place-self: end;
}

Что происходит с place-self свойство? Зачем нам это нужно и почему каждое изображение имеет определенную ценность?

Помните проблему, с которой мы столкнулись в предыдущей статье, когда создание сетки из кусочков головоломки? Мы увеличили размер изображений, чтобы создать переполнение, но у некоторых изображений переполнение было неправильным. Мы исправили их с помощью place-self имущество.

Та же проблема здесь. Мы увеличиваем размер изображений, чтобы каждое из них выходило за пределы ячеек сетки. Но если мы ничего не сделаем, все они переполнятся в правой и нижней части сетки. Что нам нужно:

  1. первое изображение, выходящее за правый нижний край (поведение по умолчанию),
  2. второе изображение выходит за нижний левый край,
  3. третье изображение, которое выходит за верхний правый край, и
  4. четвертое изображение выходит за верхний левый край.

Для этого нам нужно правильно разместить каждое изображение, используя place-self имущество.

Диаграмма, показывающая значения свойств Place-Self для каждого квадранта сетки.
CSS-сетка и пользовательские фигуры, часть 2

Если вы не знакомы с place-self, это сокращение от justify-self и align-self разместить элемент горизонтально и вертикально. Если он принимает одно значение, оба выравнивания используют одно и то же значение.

Расширение панелей изображений

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

Особым случаем стали классические расширяющиеся панели, где у нас есть только один ряд и контейнер во всю ширину.

Мы возьмем этот пример и объединим его с фигурами!

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

Во-первых, давайте начнем с упрощения кода и удаления некоторых переменных.

Нам нужна только одна строка, а количество столбцов должно корректироваться в зависимости от количества изображений. Это означает, что нам больше не нужны переменные для количества строк (--n) и столбцы (--m ), но нам нужно использовать grid-auto-flow: column, что позволяет сетке автоматически генерировать столбцы по мере добавления новых изображений. Мы будем считать фиксированную высоту нашего контейнера; по умолчанию он будет полной ширины.

Давайте обрежем изображения наклонной формой:

Снимок головы спокойного красного волка, смотрящего вниз, с наложенными вершинами, показывающими точки свойств клип-пути.
clip-path: polygon(S 0%, 100% 0%, (100% - S) 100%, 0% 100%);

Еще раз: каждое изображение содержится в своей ячейке сетки, поэтому между изображениями больше места, чем хотелось бы:

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

Нам нужно увеличить ширину изображений, чтобы создать перекрытие. Мы заменяем min-width: 100% min-width: calc(100% + var(--s)), Где --s — новая переменная, управляющая формой.

Теперь нам нужно исправить первое и последнее изображения, чтобы они как бы выходили за пределы страницы без пробелов. Другими словами, мы можем удалить наклон с левой стороны первого изображения и наклон с правой стороны последнего изображения. Нам нужен новый clip-path специально для этих двух изображений.

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

.gallery > img:first-child {
  min-width: calc(100% + var(--s)/2);
  place-self: start;
  clip-path: polygon(0 0,100% 0,calc(100% - var(--s)) 100%,0 100%);
}
.gallery > img:last-child {
  min-width: calc(100% + var(--s)/2);
  place-self: end;
  clip-path: polygon(var(--s) 0,100% 0,100% 100%,0 100%);
}

Конечным результатом является красивая расширяющаяся панель наклонных изображений!

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

Мы могли бы сделать такой же макет с помощью flexbox, поскольку имеем дело с одной строкой элементов. Вот моя реализация.

Конечно, наклонные изображения — это круто, но как насчет зигзагообразного рисунка? Я уже дразнил это на конец последней статьи.

Все, что я здесь делаю, это заменяю clip-path mask… И угадайте, что? У меня уже есть подробная статья создавая эту зигзагообразную форму — не говоря уже об онлайне генератор для получения кода. Видите, как все сходится?

Самая сложная часть здесь — убедиться, что зигзаги идеально выровнены, а для этого нам нужно добавить смещение для каждого :nth-child(odd) элемент изображения.

.gallery > img {
  mask: 
    conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg) 
      100% calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y,
    conic-gradient(from   45deg at left,  #0000, #000 1deg 89deg, #0000 90deg) 
      0%   calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y;
}
/* we add an offset to the odd elements */
.gallery > img:nth-child(odd) {
  --_p: var(--s);
}
.gallery > img:first-child {
  mask: 
    conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg) 
      0 calc(50% + var(--_p, 0%))/100% calc(2*var(--s));
}
.gallery > img:last-child {
  mask: 
    conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg) 
      0 calc(50% + var(--_p, 0%)) /100% calc(2*var(--s));
}

Обратите внимание на использование --_p переменная, которая вернется к 0% но будет равен --_s для странных изображений.

Вот демо, которое иллюстрирует проблему. Наведите указатель мыши, чтобы увидеть, как смещение, определенное --_p — исправляет расстановку.

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

А почему не закругленные бока? Давай сделаем это!

Я знаю, что код может выглядеть пугающим и сложным для понимания, но все, что происходит, — это комбинация различных приемов, которые мы рассмотрели в этой и других статьях, которыми я уже поделился. В этом случае я использую ту же структуру кода, что и для зигзагообразных и наклонных фигур. Сравните его с этими примерами, и вы не найдете разницы! Это те же приемы моя предыдущая статья об эффекте масштабирования. Затем я использую свой другое письмо и мой онлайн-генератор чтобы получить код маски, которая создает эти закругленные формы.

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

Первая маска:

mask: 
  linear-gradient(-90deg,#0000 calc(2*var(--s)),#000 0) var(--s),
  radial-gradient(var(--s),#000 98%,#0000) 50% / calc(2*var(--s)) calc(1.8*var(--s)) space repeat;
CSS Grid и пользовательские фигуры, часть 2. Анализ данных PlatoBlockchain. Вертикальный поиск. Ай.

Второй:

mask:
  radial-gradient(calc(var(--s) + var(--g)) at calc(var(--s) + var(--g)) 50%,#0000 98% ,#000) 
  calc(50% - var(--s) - var(--g)) / 100% calc(1.8*var(--s))
CSS Grid и пользовательские фигуры, часть 2. Анализ данных PlatoBlockchain. Вертикальный поиск. Ай.

Единственное, что я здесь сделал, — это обновил вторую маску, включив в нее переменную разрыва (--g), чтобы создать пространство между изображениями.

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

Для первого изображения мы всегда знаем, какая маска ему нужна, а именно:

.gallery > img:first-child {
  mask: 
    radial-gradient(calc(var(--s) + var(--g)) at right, #0000 98%, #000) 50% / 100% calc(1.8 * var(--s));
}
Фотография бурого медведя с волнистым узором на правой границе.
CSS-сетка и пользовательские фигуры, часть 2

Для последнего изображения это зависит от количества элементов, поэтому имеет значение, является ли этот элемент :nth-child(odd) or :nth-child(even).

Полная сетка фотографий диких животных со всеми правильными границами и промежутками между изображениями.
CSS-сетка и пользовательские фигуры, часть 2
.gallery > img:last-child:nth-child(even) {
  mask: 
    linear-gradient(to right,#0000 var(--s),#000 0),
    radial-gradient(var(--s),#000 98%,#0000) left / calc(2*var(--s)) calc(1.8*var(--s)) repeat-y
}
Однорядная сетка из трех фотографий диких животных с волнистыми границами, где последнее изображение представляет собой элемент с нечетным номером.
CSS-сетка и пользовательские фигуры, часть 2
.gallery > img:last-child:nth-child(odd) {
  mask: 
    radial-gradient(calc(var(--s) + var(--g)) at left,#0000 98%,#000) 50% / 100% calc(1.8*var(--s))
}

Вот и все! Три разных макета, но каждый раз одни и те же приемы CSS:

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

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

А вот реализация Flexbox

Подведение итогов

Уф, мы закончили! Я знаю, что между этой и предыдущей статьями есть много приемов и примеров CSS, не говоря уже обо всех других приемах, на которые я ссылался здесь из других написанных мною статей. Мне потребовалось время, чтобы собрать все воедино, да и не обязательно понимать все сразу. Одно прочтение даст вам хороший обзор всех макетов, но, возможно, вам придется прочитать статью несколько раз и сосредоточиться на каждом примере, чтобы понять все хитрости.

Вы заметили, что мы вообще не коснулись HTML, за исключением, возможно, количества изображений в разметке? Все созданные нами макеты используют один и тот же HTML-код, который представляет собой не что иное, как список изображений.

Прежде чем закончить, я оставлю вам последний пример. Это «против» между двумя аниме-персонажами с крутым эффектом наведения.

А вы? Можете ли вы создать что-то на основе того, что вы узнали? Это не обязательно должно быть сложно — представьте себе что-нибудь крутое или забавное, как я это сделал в том аниме-матче. Это может стать для вас хорошим упражнением, а в конце мы опубликуем отличную подборку в разделе комментариев.

Отметка времени:

Больше от CSS хитрости