Создавайте адаптивные эффекты изображения с помощью 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, включая функции и эффекты, которые мы рассмотрели сегодня:
Ищете больше? Обязательно ознакомьтесь с нашим руководством по CSS здесь, на Smashing →