Создать сетку изображений легко благодаря CSS Grid. Но заставить сетку делать причудливые вещи после изображения были размещены, может быть сложно осуществить.
Скажем, вы хотите добавить какой-нибудь причудливый эффект наведения к изображениям, где они увеличиваются и масштабируются за пределы строк и столбцов, в которых они расположены? Мы можем это сделать!
Круто, правда? Если вы проверите код, вы не найдете ни JavaScript, ни сложных селекторов, ни даже магические числа. И это только один пример из многих, которые мы рассмотрим!
Построение сетки
Код HTML для создания сетки так же прост, как список изображений в контейнере. Нам не нужно больше, чем это.
<div class="gallery">
<img>
<img>
<img>
<!-- etc. -->
</div>
Для CSS мы сначала начнем с настройки сетки, используя следующее:
.gallery {
--s: 150px; /* controls the size */
--g: 10px; /* controls the gap */
display: grid;
gap: var(--g);
width: calc(3*var(--s) + 2*var(--g)); /* 3 times the size plus 2 times the gap */
aspect-ratio: 1;
grid-template-columns: repeat(3, auto);
}
Короче говоря, у нас есть две переменные, одна из которых управляет размером изображений, а другая задает размер промежутка между изображениями. aspect-ratio
помогает сохранить пропорции.
Вам может быть интересно, почему мы определяем только три столбца, но не строки. Нет, я не забыл строки — нам просто не нужно их явно задавать. CSS Grid может автоматически размещать элементы на неявные строки и столбцы, что означает, что мы получаем столько строк, сколько необходимо для любого количества изображений, которые мы ему подбрасываем. Вместо этого мы можем явно определить строки, но нам нужно добавить grid-auto-flow: column
чтобы убедиться, что браузер создаст для нас необходимые столбцы.
Вот пример, иллюстрирующий оба случая. Разница в том, что один течет в row
направление и другое в column
направлении.
Узнать подробности эта другая статья, которую я написал для получения дополнительной информации о неявных сетках и алгоритме автоматического размещения.
Теперь, когда у нас есть сетка, пришло время стилизовать изображения:
.gallery > img {
width: 0;
height: 0;
min-height: 100%;
min-width: 100%;
object-fit: cover;
}
Эффект наведения, который мы делаем, зависит от этого CSS. Возможно, вам покажется странным, что мы делаем изображения, которые не имеют ни ширины, ни высоты, но имеют минимальную ширину и высоту, равные 100%. Но вы увидите, что это довольно ловкий трюк для того, чего мы пытаемся достичь.
Что я здесь делаю, так это сообщаю браузеру, что изображения должны иметь 0
ширина и высота, но также должны иметь минимальную высоту, равную 100%
… но 100%
которого? При использовании процентов значение равно относительно чего-то другого. В этом случае наше изображение помещается внутри ячейка сетки и нам нужно знать этот размер, чтобы знать, что 100%
является относительным.
Браузер сначала проигнорирует min-height: 100%
для вычисления размера ячеек сетки, но он будет использовать height: 0
в его расчете. Это означает, что наши изображения не будут влиять на размер ячеек сетки… потому что технически они не имеют физического размера. Это приведет к трем равным столбцам и строкам, которые основаны на размере сетки (которую мы определили на .gallery
ширина и aspect-ratio
). Высота каждой ячейки сетки не что иное, как переменная --s
мы определили (то же самое для ширины).
Теперь, когда у нас есть размеры ячеек нашей сетки, браузер будет использовать их с min-height: 100%
(И min-width: 100%
), что заставит изображения полностью заполнить пространство каждой ячейки сетки. Все это может показаться немного запутанным, но основная идея состоит в том, чтобы убедиться, что сетка определяет размер изображений, а не наоборот. Я не хочу, чтобы изображение определяло размер сетки, и вы поймете, почему после добавления эффекта наведения.
Создание эффекта наведения
Что нам нужно сделать, так это увеличить масштаб изображений при наведении курсора. Мы можем сделать это, настроив изображение width
и height
on :hover
:
.gallery {
--f: 1.5; /* controls the scale factor */
}
.gallery img:hover{
width: calc(var(--s) * var(--f));
height: calc(var(--s) * var(--f));
}
Я добавил новую пользовательскую переменную, --f
, в качестве коэффициента масштабирования для управления размером при наведении. Обратите внимание, как я умножаю переменную size, --s
, чтобы вычислить новый размер изображения.
Но вы сказали, что размер изображения должен быть равен 0. Что происходит? Я потерян…
То, что я сказал, по-прежнему верно, но я делаю исключение для зависшего изображения. Я говорю браузеру, что только одно изображение будет иметь размер, отличный от нуля, поэтому оно будет вносить вклад в размерность сетки, а все остальные останутся равными 0
.
Левая сторона показывает сетку в ее естественном состоянии без каких-либо наведенных изображений, что и показывает правая сторона. Все ячейки сетки слева имеют одинаковый размер, так как все изображения не имеют физических размеров.
С правой стороны наведено второе изображение в первой строке, что придает ему размеры, влияющие на размер ячейки сетки. Браузер будет увеличивать эту конкретную ячейку сетки при наведении курсора, что влияет на общий размер. А так как задан размер всей сетки (потому что мы задаем фиксированный width
на .gallery
), другие ячейки сетки будут логично реагировать, становясь меньше, чтобы сохранить .gallery
общий размер в такт.
Это наш эффект масштабирования в действии! Увеличивая размер только одного изображения, мы влияем на всю конфигурацию сетки, и мы говорили ранее, что сетка определяет размер изображений, так что каждое изображение растягивается внутри своей ячейки сетки, чтобы заполнить все пространство.
К этому мы добавляем немного transition
И использовать object-fit
чтобы избежать искажения изображения, и иллюзия идеальна!
Я знаю, что логику этого трюка понять непросто. Не волнуйтесь, если вы не полностью понимаете это. Самое важное — понять структуру используемого кода и то, как его модифицировать, чтобы получить больше вариаций. Это то, что мы будем делать дальше!
Добавление дополнительных изображений
Мы создали сетку 3×3, чтобы объяснить основной трюк, но вы, наверное, догадались, что нам не нужно останавливаться на достигнутом. Мы можем сделать количество столбцов и строк переменными и добавить столько изображений, сколько захотим.
.gallery {
--n: 3; /* number of rows*/
--m: 4; /* number of columns */
--s: 150px; /* control the size */
--g: 10px; /* control the gap */
--f: 1.5; /* control the scale factor */
display: grid;
gap: var(--g);
width: calc(var(--m)*var(--s) + (var(--m) - 1)*var(--g));
height: calc(var(--n)*var(--s) + (var(--n) - 1)*var(--g));
grid-template-columns: repeat(var(--m),auto);
}
У нас есть две новые переменные для количества строк и столбцов. Затем мы просто определяем ширину и высоту нашей сетки, используя их. То же самое для grid-template-columns
который использует --m
переменная. И, как и раньше, нам не нужно явно определять строки, поскольку функция автоматического размещения CSS Grid сделает всю работу за нас, независимо от того, сколько элементов изображения мы используем.
Почему не разные значения ширины и высоты? Мы можем это сделать:
.gallery {
--n: 3; /* number of rows*/
--m: 4; /* number of columns */
--h: 120px; /* control the height */
--w: 150px; /* control the width */
--g: 10px; /* control the gap */
--f: 1.5; /* control the scale factor */
display: grid;
gap: var(--g);
width: calc(var(--m)*var(--w) + (var(--m) - 1)*var(--g));
height: calc(var(--n)*var(--h) + (var(--n) - 1)*var(--g));
grid-template-columns: repeat(var(--m),auto);
}
.gallery img:hover{
width: calc(var(--w)*var(--f));
height: calc(var(--h)*var(--f));
}
Мы заменяем --s
с двумя переменными, одна для ширины, --w
, и еще один для высоты, --h
. Затем мы соответствующим образом настраиваем все остальное.
Итак, мы начали с сетки с фиксированным размером и количеством элементов, но затем создали новый набор переменных, чтобы получить любую желаемую конфигурацию. Все, что нам нужно сделать, это добавить столько изображений, сколько мы хотим, и соответствующим образом настроить переменные CSS. Комбинации безграничны!
Полноэкранная галерея изображений
А полноэкранная версия? Да, это тоже возможно. Все, что нам нужно, это знать, какие значения мы должны присвоить нашим переменным. Если мы хотим N
ряды изображений, и мы хотим, чтобы наша сетка была полноэкранной, нам сначала нужно решить для высоты 100vh
:
var(--n) * var(--h) + (var(--n) - 1) * var(--g) = 100vh
Та же логика для ширины, но с использованием vw
вместо vh
:
var(--m) * var(--w) + (var(--m) - 1) * var(--g) = 100vw
Делаем математику, чтобы получить:
--w: (100vw - (var(--m) - 1) * var(--g)) / var(--m)
--h: (100vh - (var(--n) - 1) * var(--g)) / var(--n)
Готово!
Это точно такой же HTML, но с некоторыми обновленными переменными, которые изменяют размер и поведение сетки.
Обратите внимание, что я опустил формулу, которую мы ранее установили на .gallery
«s width
и height
и заменил их 100vw
и 100vh
, соответственно. Формула даст нам тот же результат, но, поскольку мы знаем, какое значение нам нужно, мы можем избавиться от всей этой дополнительной сложности.
Мы также можем упростить --h
и --w
удалив пробел из уравнения в пользу этого:
--h: calc(100vh / var(--n)); /* Viewport height divided by number of rows */
--w: calc(100vw / var(--m)); /* Viewport width divided by number of columns */
Это приведет к тому, что наведенное изображение увеличится немного больше, чем в предыдущем примере, но это не имеет большого значения, поскольку мы можем контролировать масштаб с помощью --f
переменная, которую мы используем в качестве множителя.
А так как переменные используются в одном месте, мы можем упростить код, удалив их совсем:
Важно отметить, что эта оптимизация применяется только к полноэкранному примеру, а не к примерам, которые мы рассмотрели. Этот пример является частным случаем, когда мы можем сделать код легче, удалив часть сложной вычислительной работы, необходимой в других примерах.
На самом деле у нас есть все необходимое для создания популярного шаблона расширяющихся панелей:
Давайте копнем еще глубже
Вы заметили, что наш коэффициент масштабирования может быть меньше, чем 1
? Мы можем определить размер наводимого изображения меньше, чем --h
or --w
но изображение становится больше при наведении.
Начальный размер ячейки сетки равен --w
и --h
, так почему же меньшие значения делают ячейку сетки больший? Не должна ли клетка получить меньше, или хотя бы сохранить свой первоначальный размер? И каков окончательный размер ячейки сетки?
Нам нужно углубиться в то, как алгоритм CSS Grid вычисляет размер ячеек сетки. И это включает в себя понимание CSS Grid по умолчанию растянуть выравнивание.
Вот пример для понимания логики.
В левой части демонстрации я определил два столбца с auto
ширина. Получаем интуитивно понятный результат: два равных столбца (и две равные ячейки сетки). Но сетка, которую я установил в правой части демонстрации, где я обновляю выравнивание, используя place-content: start
, кажется, ничего.
DevTools помогает показать нам, что на самом деле происходит в обоих случаях:
Во второй сетке у нас есть два столбца, но их ширина равна нулю, поэтому мы получаем две ячейки сетки, свернутые в верхнем левом углу контейнера сетки. Это не ошибка, а логический результат выравнивания сетки. Когда мы измеряем столбец (или строку) с помощью auto
, это означает, что его содержимое диктует его размер — но у нас есть пустой div
без содержания, для которого можно было бы освободить место.
Но так как stretch
является выравниванием по умолчанию, и у нас достаточно места внутри нашей сетки, браузер равномерно растянет обе ячейки сетки, чтобы покрыть всю эту область. Вот так сетка слева состоит из двух равных столбцов.
от спецификация:
Обратите внимание, что некоторые значения
justify-content
иalign-content
может привести к разнесению дорожек (space-around
,space-between
,space-evenly
) или изменить размер (stretch
).
Обратите внимание на ключевое слово «изменить размер». В последнем примере я использовал place-content
что является сокращением для justify-content
и align-content
И это зарыто где-то в алгоритм определения размера сетки спецификации:
Этот шаг расширяет дорожки, которые имеют автоматический функция максимального размера дорожки путем деления любого оставшегося положительного, определенный свободное пространство поровну среди них. Если свободное место неопределенный, Но сетка-контейнер имеет определенный мин-ширина/высота, используйте этот размер для расчета свободного места для этого шага.
«Ровно» объясняет, почему мы получаем одинаковые ячейки сетки, но это относится и к «свободному пространству», что очень важно.
Возьмем предыдущий пример и добавим содержимое в один из div
s:
Мы добавили квадрат 50px
изображение. Вот иллюстрация того, как каждая сетка в нашем примере реагирует на это изображение:
В первом случае мы видим, что первая ячейка (красная) больше второй (синяя). Во втором случае размер первой ячейки изменяется, чтобы соответствовать физическому размеру изображения, а вторая ячейка остается без размеров. Свободное пространство разделено поровну, но в первой ячейке больше содержимого, что делает ее больше.
Это математика, чтобы выяснить наше свободное пространство:
(grid width) - (gap) - (image width) = (free space)
200px - 5px - 50px = 145px
Делим на два — количество столбцов — получаем ширину 72.5px
для каждого столбца. Но мы добавляем размер изображения, 50px
, в первый столбец, который оставляет нам один столбец в 122.5px
а второй равен 72.5px
.
Та же логика применима к нашей сетке изображений. Все изображения имеют размер, равный 0
(без содержимого), в то время как наведенное изображение влияет на размер, даже если оно просто 1px
— сделать свою ячейку сетки больше остальных. По этой причине коэффициент масштабирования может быть любым значением больше, чем 0
даже десятичные дроби между 0
и 1
.
Чтобы получить окончательную ширину ячеек сетки, мы делаем тот же расчет, чтобы получить следующее:
(container width) - (sum of all gaps) - (hovered image width) = (free space)
Ширина контейнера определяется:
var(--m)*var(--w) + (var(--m) - 1)*var(--g)
…и все промежутки равны:
(var(--m) - 1)*var(--g)
… и для зависшего изображения у нас есть:
var(--w)*var(--f)
Мы можем вычислить все это с помощью наших переменных:
var(--m)*var(--w) - var(--w)*var(--f) = var(--w)*(var(--m) - var(--f))
Количество столбцов определяется --m
, поэтому мы делим это свободное пространство поровну, чтобы получить:
var(--w)*(var(--m) - var(--f))/var(--m)
… что дает нам размер ненаведенных изображений. Для наведенных изображений у нас есть это:
var(--w)*(var(--m) - var(--f))/var(--m) + var(--w)*var(--f)
var(--w)*((var(--m) - var(--f))/var(--m) + var(--f))
Если мы хотим контролировать окончательный размер наводимого изображения, мы рассмотрим приведенную выше формулу, чтобы получить точный размер, который нам нужен. Если, например, мы хотим, чтобы изображение было в два раза больше:
(var(--m) - var(--f))/var(--m) + var(--f) = 2
Итак, значение нашего масштабного множителя, --f
, должно быть равно:
var(--m)/(var(--m) - 1)
Для трех столбцов будем иметь 3/2 = 1.5
и это коэффициент масштабирования, который я использовал в первой демонстрации этой статьи, потому что я хотел сделать изображение в два раза больше при наведении курсора!
Та же логика применима к вычислению высоты, и в случае, если мы хотим управлять ими обоими независимо, нам нужно будет учитывать два коэффициента масштабирования, чтобы убедиться, что у нас есть определенная ширина и высота при наведении.
.gallery {
/* same as before */
--fw: 1.5; /* controls the scale factor for the width */
--fh: 1.2; /* controls the scale factor for the height */
/* same as before */
}
.gallery img:hover{
width: calc(var(--w)*var(--fw));
height: calc(var(--h)*var(--fh));
}
Теперь вы знаете все секреты создания любой сетки изображений с классным эффектом наведения, а также можете контролировать размер, который вы хотите, используя математику, которую мы только что рассмотрели.
Подведение итогов
В моем Последняя статья, мы создали сложную сетку с помощью нескольких строк CSS, которые задействовали неявную сетку CSS Grid и функции автоматического размещения. В этой статье мы полагались на некоторые трюки с размером CSS Grid, чтобы создать причудливую сетку изображений, которые масштабируются при наведении и заставляют сетку соответствующим образом корректироваться. Все это с помощью упрощенного кода, который легко настроить с помощью переменных CSS!
В следующей статье мы поиграем с формами! Мы объединим сетку CSS с маской и clip-path, чтобы получить причудливую сетку изображений.