Как сделать обтекание картинки текстом в CSS

Чтобы текст обтекал картинку - как в газетной вёрстке, - изображению нужно свойство float: left или float: right. Картинка "прилипает" к нужному краю, а весь текст рядом с ней сам обтекает её без единой обёртки или flex/grid-конструкции. У этого древнего свойства до сих пор есть одна задача, с которой не справляются ни Flexbox, ни Grid - именно она разбирается в этой статье.

Как работает float: картинка слева или справа от текста

float выводит элемент из обычного потока документа и прижимает его к левому или правому краю родителя. Блочные элементы после него встают на своё обычное место, как будто float-элемента нет вообще, а вот строчное содержимое - текст - огибает его, занимая свободное место сбоку. Именно поэтому эффект обтекания работает только с текстом (и другим инлайн-содержимым), а не с соседними блоками.

Отступ между картинкой и текстом задаётся margin'ом на самой float-картинке - margin на абзаце текста картинку не отодвинет, потому что текст технически "подтекает" под неё, а не стоит с ней в одном ряду. Ещё один нюанс: применение float автоматически превращает элемент в блочный по вычисляемому display, даже если это был изначально строчный img.

float: left; у картинки - текст абзаца обтекает её справа.

margin-left: 20px; на самом абзаце вместо margin на картинке - отступ не появится, картинка и текст всё равно будут слипаться.

Пример: как float обтекает текст

Результат
IMG

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

CSS
.article-image {
  float: left;
  margin: 0 16px 8px 0;
}
/* для правого края: float: right; margin: 0 0 8px 16px; */

Проблема "схлопывания" контейнера и три способа её решить

Если внутри контейнера ВСЕ дочерние элементы float-нутые, у родителя схлопывается высота почти до нуля - ведь float выводит элементы из потока, и родитель их как бы не "видит" при расчёте своей высоты. На практике это выглядит так: фон или рамка контейнера обрывается там, где должны быть картинки, а сам контейнер выглядит пустым.

Есть три способа заставить родителя учитывать float-детей:

Пример: как flow-root решает схлопывание

Результат

Без исправления - пунктирная рамка обрывается сразу, "не замечая" картинку:

IMG

С display: flow-root - рамка честно обхватывает картинку по высоте:

IMG
CSS
/* Старый способ - clearfix-хак через псевдоэлемент */
.wrapper::after {
  content: "";
  display: block;
  clear: both;
}
 
/* Способ через overflow - тоже работает, но может обрезать
   тень или добавить нежелательный скролл */
.wrapper {
  overflow: auto;
}
 
/* Современный способ - без побочных эффектов */
.wrapper {
  display: flow-root;
}

clearfix-хак (первый вариант) держится на том, что генерируемый ::after-элемент получает clear: both и как бы "продавливает" реальную высоту контейнера ниже float-детей - долгое время это был единственный способ, отсюда и название. overflow: auto тоже "исправляет" схлопывание, потому что создаёт новый блочный форматирующий контекст (BFC), но как побочный эффект может обрезать тени или показать лишний скроллбар. display: flow-root создан специально для этой задачи - никаких побочных эффектов, поддержка около 96% браузеров по данным caniuse, и это рекомендуемый способ на сегодняшний день.

float внутри Flexbox и Grid: почему свойство перестаёт действовать

float игнорируется у элементов, которые сами являются flex- или grid-элементами - то есть у прямых детей контейнера с display: flex или display: grid. Если добавить float ребёнку flex-контейнера, ничего не произойдёт: расположением там управляет сам flex/grid-алгоритм, а float для этих детей просто не применяется.

Пример: float молча игнорируется во flex-контейнере

Результат

У блока ниже стоит float: left, но родитель - display: flex, поэтому float ни на что не влияет, элемент встаёт как обычный flex-элемент:

float: left

Текст рядом не обтекает картинку - он просто соседний flex-элемент.

CSS
.card {
  display: flex; /* родитель - flex-контейнер */
}
.card img {
  float: left; /* не сработает: это flex-элемент, float игнорируется */
}

Если внутри карточки на Flexbox/Grid всё-таки нужно обтекание текстом вокруг картинки, float стоит применять не к самому flex/grid-элементу, а к вложенному тексту-блоку внутри одной из ячеек - то есть создавать обычный поток текста уже внутри конкретной flex/grid-области, а не пытаться float-ить сам flex-элемент.

Обтекание по контуру: shape-outside для не-прямоугольных картинок

По умолчанию текст обтекает прямоугольную область картинки, даже если на самой картинке круглый или неровный силуэт. Свойство shape-outside меняет форму этой невидимой границы обтекания - но работает оно только вместе с float, применяется исключительно к float-элементам.

Поддерживаются готовые фигуры circle(), ellipse(), polygon(), inset(), а также реальное изображение через url() - в этом случае граница вычисляется по альфа-каналу картинки (какие пиксели прозрачные), и порог прозрачности можно настроить свойством shape-image-threshold. Поддержка широкая, Baseline-стабильная с января 2020 года.

Пример: как работает shape-outside

Результат

Текст здесь обтекает не прямоугольник картинки, а её реальную круглую форму - буквы подходят ближе к кругу сверху и снизу, а не останавливаются на невидимом квадратном углу. Это и есть эффект shape-outside: circle(50%).

CSS
.round-photo {
  float: left;
  shape-outside: circle(50%);
  border-radius: 50%;
  width: 110px;
  height: 110px;
}

Сравнение способов

Приёмы обтекания текстом и работы с float
Приём Когда использовать Поддержка браузерами
float: left / float: right Обтекание текста вокруг картинки, как в газетной вёрстке Все браузеры
Clearfix (::after + clear: both) Исправить схлопывание контейнера старым способом, для очень старых браузеров Все браузеры
overflow: auto Быстро исправить схлопывание, если не важны побочные эффекты (обрезка тени, скролл) Все браузеры
display: flow-root Исправить схлопывание контейнера без побочных эффектов (рекомендуемый способ) Около 96% браузеров (Baseline)
shape-outside Обтекание по контуру не-прямоугольной картинки, только вместе с float Baseline с января 2020 года

Частые ошибки

ОшибкаЧто происходитКак исправить
margin задан абзацу текста, а не картинкеОтступ не появляется, текст всё равно "слипается" с картинкойЗадать margin на самой float-картинке
Все дочерние элементы контейнера - float, а у самого контейнера нет фиксаВысота родителя схлопывается почти до нуля, фон или рамка обрываютсяДобавить контейнеру display: flow-root (или clearfix/overflow: auto)
float задан прямому flex- или grid-элементуСвойство молча игнорируется, обтекания нетПрименять float к вложенному тексту внутри ячейки, а не к самому flex/grid-элементу
shape-outside указан без floatФорма обтекания не применяется, картинка остаётся прямоугольнойДобавить float: left или float: right тому же элементу

Подробнее о том, что такое блочные и строчные элементы и как свойство display определяет их поведение на странице - в уроке курса Блочные и строчные элементы: display block, inline, inline-block. float, как показано выше, сам меняет вычисляемый display элемента на блочный - это прямое продолжение темы того урока.

Категория
Поделиться в социальных сетях

Отправить комментарий

0 Комментарии

Отправить комментарий (0)

#buttons=(Хорошо) #days=(20)

Мы используем файлы cookie для улучшения работы сайта. Подробнее
Хорошо
×