Создавайте адаптивные эффекты изображения с помощью CSS-градиентов и соотношения сторон
Опубликовано: 2022-03-10aspect-ratio в сочетании с object-fit обеспечивает решение этой головной боли прошлого! Давайте научимся использовать эти свойства в дополнение к созданию адаптивного эффекта градиентного изображения для дополнительного эффекта.Чтобы подготовиться к нашим будущим эффектам изображения, мы собираемся настроить компонент карты, который имеет большое изображение вверху, за которым следуют заголовок и описание. Общая проблема с этой настройкой заключается в том, что мы не всегда имеем полный контроль над тем , что представляет собой изображение, и, что более важно для нашего макета, каковы его размеры . И хотя это можно решить, обрезав заранее, мы все еще можем столкнуться с проблемами из-за контейнеров с гибкими размерами. Следствием этого является неравномерное расположение содержимого карты, которое действительно выделяется, когда вы предъявляете ряд карт.
Другим предыдущим решением, помимо обрезки, мог быть переход от встроенного img к пустому div , который существовал только для представления изображения через background-image . Я реализовал это решение много раз в прошлом. Одним из преимуществ этого является использование старого трюка для пропорций, который использует элемент нулевой высоты и устанавливает значение padding-bottom . Установка значения отступа в процентах приводит к конечному вычисляемому значению, которое относится к ширине элемента. Возможно, вы также использовали эту идею, чтобы поддерживать соотношение сторон 16:9 для встраивания видео, и в этом случае значение отступа находится по формуле: 9/16 = 0.5625 * 100% = 56.26% . Но мы собираемся изучить два современных свойства CSS , которые не требуют дополнительной математики, дают нам больше гибкости, а также позволяют сохранить семантику, обеспечиваемую использованием реального img вместо пустого div .
Во-первых, давайте определим семантику HTML, включая использование неупорядоченного списка в качестве контейнера карт:
<ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul> Далее мы создадим минимальный набор базовых стилей для компонента .card . Мы установим некоторые основные визуальные стили для самой карты, быстро обновим ожидаемый заголовок h3 , затем основные стили, чтобы приступить к оформлению изображения карты.
.card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; } Последнее правило использует общий комбинатор братьев и сестер, чтобы добавить горизонтальное поле к любому элементу, следующему за img , поскольку мы хотим, чтобы само изображение было на одном уровне с краями карты.
И наш прогресс на данный момент приводит нас к следующему виду карты:

Наконец, мы создадим стили .card-wrapper для быстрого адаптивного макета с использованием сетки CSS. Это также удалит стили списка по умолчанию.
.card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }Примечание . Если этот метод сетки вам незнаком, просмотрите объяснение в моем руководстве о современных решениях для сетки из 12 столбцов.
С этим применением и со всеми карточками, содержащими изображение с действительным исходным путем, наши .card-wrapper дают нам следующий макет:

Как показано на изображении для предварительного просмотра, этих базовых стилей недостаточно для правильного размещения изображений с учетом их различных естественных размеров. Нам нужен метод, чтобы ограничить эти изображения единообразно и последовательно.
Включить унифицированные размеры изображений с object-fit
Как отмечалось ранее, вы, возможно, ранее внесли обновление в этот сценарий, чтобы изменить изображения, которые будут добавляться с помощью background-image вместо этого, и использовать background-size: cover для корректного изменения размера изображения. Или вы, возможно, пытались принудительно обрезать кадрирование заранее (все равно достойная цель, поскольку любое уменьшение размера изображения улучшит производительность!).
Теперь у нас есть доступное свойство object-fit , которое позволяет тегу img выступать в качестве контейнера для изображения. Кроме того, оно имеет значение cover , которое приводит к такому же эффекту, как и решение с фоновым изображением, но с дополнительным преимуществом сохранения семантики встроенного изображения. Давайте применим его и посмотрим, как это работает.
Нам нужно соединить его с размером height , чтобы получить дополнительное представление о том, как мы хотим, чтобы контейнер изображения вел себя (напомним, что мы уже добавили width: 100% ). И мы собираемся использовать функцию max() для выбора либо 10rem либо 30vh в зависимости от того, что больше в данном контексте, что предотвращает слишком сильное уменьшение высоты изображения на меньших окнах просмотра или когда пользователь установил большое масштабирование.
img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); } Дополнительный совет по специальным возможностям : всегда следует тестировать макеты с масштабом 200 % и 400 % на рабочем столе. Хотя в настоящее время не существует медиа-запроса zoom , такие функции, как max() , могут помочь решить проблемы с макетом. Еще один контекст, в котором этот метод полезен, — это расстояние между элементами.
С этим обновлением мы определенно улучшили ситуацию, и визуальный результат выглядит так, как если бы мы использовали старую технику фонового изображения:

Отзывчиво согласованный размер изображения с aspect-ratio
При использовании object-fit сам по себе один недостаток заключается в том, что нам все равно нужно установить некоторые подсказки размеров.
Предстоящее свойство (в настоящее время доступное в браузерах Chromium), называемое aspect-ratio , расширит наши возможности по постоянному размеру изображений.
Используя это свойство, мы можем определить соотношение для изменения размера изображения вместо установки явных размеров. Мы продолжим использовать его в сочетании с object-fit чтобы гарантировать, что эти размеры влияют только на изображение как на контейнер, иначе изображение может выглядеть искаженным.
Вот наше полное обновленное правило изображения:
img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }Мы собираемся начать с соотношения изображения 4 ⁄ 3 для контекста нашей карты, но вы можете выбрать любое соотношение. Например, 1 ⁄ 1 для квадрата или 16 ⁄ 9 для стандартного встраивания видео.
Вот обновленные карты, хотя, вероятно, будет трудно заметить визуальную разницу в этом конкретном случае, поскольку соотношение сторон близко соответствует внешнему виду, которого мы достигли, установив height только для object-fit .

Установка «соотношения сторон» приводит к тому, что соотношение сохраняется при увеличении или уменьшении элементов, тогда как при установке только «подгонки объекта» и «высоты» соотношение изображения будет постоянно изменяться по мере изменения размеров контейнера.
“
Добавление адаптивных эффектов с помощью градиентов и функций CSS
Хорошо, теперь, когда мы знаем, как настроить изображения одинакового размера, давайте повеселимся с ними, добавив эффект градиента!

Наша цель с этим эффектом состоит в том, чтобы он выглядел так, как будто изображение растворяется в содержимом карты. У вас может возникнуть соблазн поместить изображение в отдельный контейнер, чтобы добавить градиент, но благодаря работе, которую мы уже проделали с размером изображения, мы можем решить, как безопасно сделать это на основной .card .
Первым шагом является определение градиента . Мы собираемся использовать пользовательское свойство CSS, чтобы добавить цвета градиента, чтобы можно было легко менять эффект градиента, начиная с синего и розового. Последний цвет в градиенте всегда будет белым, чтобы сохранить переход на фон содержимого карточки и создать «растушеванный» край.
.card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ } Но подождите — это функция CSS max() ? В градиенте? Да, это возможно, и это волшебство делает этот градиент эффективным и отзывчивым!
Однако, если бы я добавил скриншот, мы бы еще не увидели, что градиент оказывает какое-либо влияние на изображение. Для этого нам нужно ввести свойство mix-blend-mode , и в этом сценарии мы будем использовать значение overlay :
img { /* ...existing styles */ mix-blend-mode: overlay; } Свойство mix-blend-mode похоже на применение стилей наложения слоев, доступных в программном обеспечении для обработки фотографий, таком как Photoshop. А значение overlay позволяет средним тонам изображения смешиваться с градиентом позади него, что приводит к следующему результату:

Теперь, на данный момент, мы полагаемся только на значение aspect-ratio для изменения размера изображения. И если мы изменим размер контейнера и заставим макет карты переформатироваться, изменение высоты изображения вызовет несоответствия в том, где градиент становится белым.
Поэтому мы также добавим свойство max-height , которое также использует функцию max() и содержит значения, немного превышающие значения в градиенте. В результате градиент будет (почти всегда) правильно совпадать с нижней частью изображения.
img { /* ...existing styles */ max-height: max(10rem, 30vh); }Важно отметить, что добавление «max-height» меняет поведение «соотношения сторон». Вместо того, чтобы всегда использовать точное соотношение, оно будет использоваться только тогда, когда будет достаточно выделенного места с учетом нового дополнительного ограничения «max-height».
“
Тем не менее, aspect-ratio прежнему будет обеспечивать постоянное изменение размера изображений, как это было преимуществом по сравнению только с object-fit . Попробуйте закомментировать aspect-ratio в финальной демонстрации CodePen, чтобы увидеть разницу между размерами контейнеров.
Поскольку наша первоначальная цель состояла в том, чтобы сделать изображения с постоянно адаптируемыми размерами , мы все же достигли цели. Для вашего собственного варианта использования вам может понадобиться поиграть со значениями отношения и высоты, чтобы добиться желаемого эффекта.
Альтернативный вариант: mix-blend-mode наложения и добавление фильтра
Использование overlay в качестве значения mix-blend-mode было лучшим выбором для эффекта плавного перехода к белому, который мы искали, но давайте попробуем альтернативный вариант для более драматичного эффекта.
Мы собираемся обновить наше решение, чтобы добавить пользовательское свойство CSS для значения mix-blend-mode а также обновить значения цвета для градиента:
.card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); } Значение multiply оказывает затемняющий эффект на средние тона, но сохраняет белый и черный цвета как есть, что приводит к следующему виду:

Хотя мы потеряли затухание и теперь имеем четкий край в нижней части изображения, белая часть нашего градиента по-прежнему важна, чтобы гарантировать, что градиент заканчивается до содержимого карты.
Одна дополнительная модификация , которую мы можем добавить, — это использование filter и, в частности, использование функции grayscale() для удаления цветов изображения и, следовательно, сделать градиент единственным источником окраски изображения.
img { /* ...existing styles */ filter: grayscale(100); } Использование значения grayscale(100) приводит к полному удалению естественных цветов изображения и преобразованию его в черно-белое. Вот обновление для сравнения с предыдущим снимком экрана его эффекта при использовании нашего оранжевого градиента с multiply :

Используйте aspect-ratio как прогрессивное улучшение
Как упоминалось ранее, в настоящее время aspect-ratio поддерживается только в последних версиях браузеров Chromium (Chrome и Edge). Тем не менее, все браузеры поддерживают object-fit и это вместе с нашими ограничениями по height приводит к менее идеальному, но все же приемлемому результату, как показано здесь для Safari:

Без функции aspect-ratio результатом здесь является то, что в конечном итоге высота изображения ограничена, но естественные размеры каждого изображения по-прежнему приводят к некоторому расхождению между высотами изображений карточек. Вместо этого вы можете добавить max-height или снова использовать функцию max() , чтобы сделать max-height более чувствительным к разным размерам карточек.
Расширение эффектов градиента
Поскольку мы определили остановки цвета градиента как пользовательское свойство CSS, у нас есть доступ для их изменения в различных контекстах. Например, мы можем изменить градиент, чтобы более ярко отображать один из цветов, если карта наведена или если в фокусе находится один из ее дочерних элементов.
Во-первых, мы обновим каждую карточку h3 , чтобы она содержала ссылку, например:
<h3><a href="">A Super Wonderful Headline</a></h3> Затем мы можем использовать один из наших новейших доступных селекторов — :focus-within — для изменения градиента карты, когда ссылка находится в фокусе. Для дополнительного охвата возможных взаимодействий мы соединим это с :hover . И мы будем повторно использовать нашу идею max() , чтобы назначить один цвет, чтобы взять на себя покрытие части изображения карты. Недостатком этого конкретного эффекта является то, что остановка градиента и изменение цвета не могут быть надежно анимированы, но скоро они будут реализованы благодаря CSS Houdini.
Чтобы обновить цвет и добавить новую цветовую точку, нам просто нужно переназначить значение --card-gradient в этом новом правиле:
.card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); } Наши значения max() меньше, чем исходные значения, используемые для white , чтобы сохранить размытый край. Если бы мы использовали те же значения, он бы совпал с white и создал бы четкое разделение по линейке.
При создании этой демонстрации я первоначально попробовал эффект, который использовал transform с scale для эффекта увеличения. Но я обнаружил, что из mix-blend-mode и наложения браузер не всегда перерисовывал изображение, что вызывало неприятное мерцание. Всегда будут компромиссы при запросе от браузера выполнения эффектов и анимации только с помощью CSS, и хотя это очень здорово, что мы можем сделать, всегда лучше проверить влияние ваших эффектов на производительность .
Получайте удовольствие от экспериментов!
Современный CSS дал нам несколько замечательных инструментов для обновления наших наборов инструментов для веб-дизайна, причем aspect-ratio является последним дополнением. Так что идите вперед и экспериментируйте с object-fit , aspect-ratio и добавляйте функции, такие как max() , в свои градиенты для получения забавных адаптивных эффектов! Просто не забудьте дважды проверить все в разных браузерах (пока!) и в разных окнах просмотра и размерах контейнеров.
Вот CodePen, включая функции и эффекты, которые мы рассмотрели сегодня:
См. Pen [Эффекты адаптивного изображения с градиентами CSS и соотношением сторон] (https://codepen.io/smashingmag/pen/WNoERXo) Стефани Эклс.
Ищете больше? Обязательно ознакомьтесь с нашим руководством по CSS здесь, на Smashing →
