Перейти к содержанию

Контуры, фигуры, обтравка и маски

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

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

Контуры и фигуры

Фигуры в CSS задают функциями. Общие сведения о функциях — в модуле «Функции». Здесь речь о том, как собрать фигуру. Во всех примерах ниже фигуру передают в свойство clip-path: видимой остаётся только область внутри фигуры, и элемент визуально отличается от своего блока. Подробнее об обтравке — ниже.

Фигура в CSS бывает базовой (круг, прямоугольник, многоугольник) или контуром. Контуром описывают сложные и составные фигуры.

Базовые фигуры

circle() и ellipse()

Функции circle() и ellipse() задают круг и эллипс с радиусами относительно элемента. circle() принимает один размер или процент. По умолчанию обе функции ставят фигуру в центр элемента. Обе принимают необязательное положение после ключевого слова at: длину, процент или ключевое слово положения.

1
2
3
4
5
6
.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: circle(50%);
}

В примере выше круговой обтравочный контур задан функцией circle(). Радиус 50% даёт круг на всю ширину элемента. Функция ellipse() принимает два аргумента: горизонтальный и вертикальный радиусы.

1
2
3
4
5
6
.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25%);
}

Здесь эллиптический контур задан функцией ellipse(). Радиус 50% даёт эллипс на всю ширину элемента. В следующем примере тот же эллипс смещён: его центр находится у верхнего края элемента.

1
2
3
4
5
6
.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25% at center top);
}

rect() и inset()

Функции rect() и inset() по-разному описывают прямоугольник: положение сторон задаётся относительно сторон элемента. Так можно получить прямоугольник, который визуально не совпадает с блоком элемента по умолчанию. Обе функции необязательно принимают ключевое слово round и тогда рисуют прямоугольник со скруглёнными углами. Синтаксис тот же, что у сокращённого свойства border-radius.

Функция rect() задаёт верхнюю и нижнюю стороны относительно верхнего края элемента, а левую и правую — относительно левого края. Она принимает четыре размера или процента: верх, право, низ и лево. rect() удобна, когда прямоугольник не должен масштабироваться вместе с элементом или когда нужно сохранить те же пропорции при изменении элемента.

1
2
3
4
5
6
.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: rect(15px 75px 45px 10px);
}

В примере прямоугольный контур задан функцией rect(). Размеры отсчитываются от верхнего и левого краёв элемента, как на схеме.

Функция inset() задаёт стороны прямоугольника отступом внутрь от каждой стороны элемента. Она принимает от одной до четырёх величин — размеров или процентов, — поэтому несколько сторон можно описать сразу. inset() удобна, когда прямоугольник должен масштабироваться вместе с элементом или когда нужно зафиксировать расстояние до краёв элемента.

1
2
3
4
5
6
.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px);
}

Здесь прямоугольный контур задан функцией inset(). Размеры отсчитываются от сторон элемента.

Функции rect() и inset() необязательно принимают ключевое слово round и тогда рисуют прямоугольник со скруглёнными углами. Синтаксис тот же, что у сокращённого свойства border-radius. В следующем примере — скруглённые варианты прямоугольников из примеров выше.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.rounded-rect {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

.rounded-inset {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

polygon()

Для остальных фигур — треугольников, пятиугольников, звёзд и так далее — функция polygon() соединяет несколько точек прямыми. Она принимает список пар из двух длин или процентов. Каждая пара — точка многоугольника: первое значение — расстояние от левого края элемента, второе — от верхнего. Замыкать многоугольник не нужно: последняя точка соединяется с первой.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: polygon(
    50% 0,
    0 100%,
    100% 100%
  );
}

Три точки в примере дают треугольный обтравочный контур.

По умолчанию polygon() заливает пересекающиеся области. Это поведение меняет необязательный первый аргумент — правило заливки. Чтобы чередовать залитые и незалитые области, задайте правило evenodd. Правило по умолчанию — nonzero.

В примере выше polygon() вместе с тригонометрическими функциями строит правильные многоугольники и звёзды. Это не самый большой правильный многоугольник, который помещается в элемент, и он не отцентрирован — попробуйте добиться этого сами. Звёзды в примере также показывают правила заливки nonzero и evenodd.

Сложные фигуры

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

path()

Функция path() принимает строку в синтаксисе контура SVG. Команды описывают отрезки и кривые, из которых состоит фигура, и так можно собрать сложную форму. Править синтаксис SVG вручную неудобно, поэтому для path() лучше взять визуальный редактор, который экспортирует эту строку.

path() не использует единицы измерения CSS: все значения читаются как пиксели. Фигура, заданная path(), не подстраивается под размер элемента или контейнера. Оставляйте path() для фигур с фиксированными размерами.

shape()

Функция shape() пока не поддерживается в Firefox.

Функция shape() описывает фигуру командами, как и path(). Команды shape() — это родной синтаксис CSS, и в них можно использовать единицы измерения CSS. Поэтому фигуру, заданную shape(), можно масштабировать вместе с макетом.

В примере сердце и круг с отверстием в центре заданы функциями path() и shape(). Для обеих функций взяты одни и те же значения в пикселях, но в shape() можно было бы использовать и другие единицы CSS: проценты или единицы относительно контейнера.

Обтравка

Обтравка определяет, какие области элемента видны, — как вырезание картинки из журнала. Свойство clip-path задаёт контур этой области.

Как в примерах выше, в clip-path можно передать любую базовую фигуру или функцию контура. Свойство также принимает контур из SVG-элемента clipPath: его можно встроить в документ или вынести в отдельный файл.

На схеме видно, как clip-path у изображения меняет видимую область. Верхний контур задан функцией circle(), нижний — SVG-элементом clipPath. Круг из circle() по умолчанию стоит по центру элемента.

clip-path принимает только один контур. Чтобы обрезать элемент несколькими непересекающимися фигурами, соберите составной контур функциями path() или shape() либо используйте SVG-элемент clipPath. Для более сложных случаев вместо обтравки берут маскирование — об этом ниже.

Обтравка фигурами

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

В примере двум элементам через класс .clipped задан круговой clip-path. Контур считается относительно каждого элемента отдельно, а текст внутри clip-path не перестраивается по форме фигуры.

Опорный блок обтравочного контура

По умолчанию обтравочный контур включает рамку элемента. Для базовых функций фигур можно сузить опорный блок до области внутри рамки. Допустимые значения: stroke-box (по умолчанию) и fill-box (только область внутри рамки).

В примере у элементов широкая рамка (20px), а clip-path задан функцией inset(). Элемент, который обрезается относительно рамки, всё ещё показывает часть рамки. Элементы, которые обрезаются относительно области внутри рамки, рамку не показывают и оказываются меньше, хотя значение inset то же.

Обтравка графикой

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<img id="kitten" src="kitten.png">

<svg>
  <defs>
    <clipPath id="kitten-clip-shape">
      <circle cx="130" cy="175" r="100" />
    </clipPath>
  </defs>
</svg>

<style>
  #kitten {
    clip-path: url(#kitten-clip-shape);
  }
</style>

В примере clipPath с id kitten-clip-shape применяется к элементу <img>. SVG здесь встроен в HTML. Если SVG лежит во внешнем файле kitten-clipper.svg, на контур ссылаются так: url(kitten-clipper.svg#kitten-clip-shape).

Маскирование

Маскирование — ещё один способ решить, какие области элемента видны, а какие скрыты. Обтравка опирается на фигуры и контуры, а маска — на пиксели изображения или градиента. В отличие от обтравки, маска может делать область частично прозрачной. К элементу можно применить несколько изображений-масок и получить разные эффекты.

Маску задаёт свойство mask-image. Оно принимает одно или несколько изображений, градиентов или ссылок на элементы <mask> в SVG-документе. Несколько масок перечисляют через запятую.

1
2
3
4
.my-element {
  mask-image: url(my-mask.png),
              linear-gradient(black 0%, transparent 100%);
}

Здесь .my-element маскируется изображением PNG, а затем линейным градиентом. По умолчанию маски складываются и дают итоговую маску.

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

Альфа-маска и маска по яркости

Маску можно считать по каналу alpha или по luminance изображения. В режиме alpha к элементу применяется прозрачность каждого пикселя маски, а цвет этого пикселя не учитывается. В режиме luminance учитываются и прозрачность, и значение пикселя — насколько он светлый или тёмный. При маскировании по яркости светлые цвета считаются видимыми, тёмные — невидимыми.

Режим задаёт свойство mask-mode. По умолчанию оно равно match-source: режим выбирается по типу изображения-маски. Для изображений и градиентов это alpha. Для SVG-масок берётся значение свойства mask-type элемента <mask> или luminance, если mask-type не задан.

В примере маской служит тестовый узор с разными цветами и значениями альфа-канала. Переключайте mask-mode: режим alpha опирается на прозрачность, режим luminance — и на яркость цвета, и на прозрачность.

Дополнительные свойства маски

У масок есть дополнительные свойства, которыми уточняют поведение. Каждое принимает список значений через запятую, и значения сопоставляются со списком масок в mask-image. Если значений меньше, чем масок, список повторяется, пока значение не будет задано для каждой маски. Лишние значения отбрасываются.

Свойство Описание
mask-clip Задаёт опорный блок элемента, к которому применяется маска. По умолчанию border-box.
mask-composite Задаёт, как маски взаимодействуют, если к одному элементу применено несколько масок. По умолчанию add.
mask-origin Задаёт опорный блок, от которого отсчитывается маска. По умолчанию border-box. Ведёт себя как background-origin и принимает те же ключевые слова.
mask-position Задаёт положение маски относительно mask-origin. Принимает ключевые слова положения, например top или center, проценты, единицы размера и значения относительно ключевого слова положения. Ведёт себя как background-position и принимает те же типы аргументов.
mask-repeat Задаёт, как маска повторяется, если маскируемый элемент больше маски. По умолчанию repeat. Ведёт себя как background-repeat и принимает те же типы аргументов.
mask-size Задаёт, как маска масштабируется относительно размера маскируемого элемента. По умолчанию auto. Ведёт себя как background-size и принимает те же типы аргументов.

Сокращённое свойство mask

Несколько свойств маски можно задать сразу сокращением mask. Так проще описать несколько масок: свойства каждой группируются вместе. Сокращение mask равносильно записи свойств в таком порядке: mask-image, mask-mode, mask-position, mask-size, mask-repeat, mask-origin, mask-clip и mask-composite. Указывать все свойства не обязательно: пропущенные сбрасываются к начальному значению. На одну маску приходится до восьми свойств, поэтому рядом полезно держать полный справочник.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.longhand {
  mask-image: linear-gradient(white, black),
              linear-gradient(90deg, black, transparent);
  mask-mode: luminance, alpha;
  mask-position: bottom left, top right;
  mask-size: 50% 50%, 30% 30%;
}

.shorthand {
  mask: linear-gradient(white, black) luminance bottom left / 50% 50%,
        linear-gradient(90deg, black, transparent) alpha top right / 30% 30%;
}

В примере к каждому классу применены две маски. Первый класс задаёт их отдельными свойствами, второй — сокращением mask. Оба набора стилей эквивалентны.

Обтекание текстом плавающих элементов

Обтравка и маска меняют только видимую область внутри блока. Сам блок остаётся прежним. Плавающий элемент влияет на поток документа по исходному ограничивающему блоку, а не по видимой части. Чтобы задать обтекание, используйте свойство shape-outside вместе с обтравочным контуром.

shape-outside задаёт фигуру, вокруг которой обтекает содержимое. Это может быть любая базовая функция фигуры, но не фигура из path() или shape() и не clipPath из SVG-документа.

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

Фигуры в анимации

Анимация clip-path

Свойство clip-path можно анимировать и плавно переходить от фигуры к фигуре. В каждом ключевом кадре должна быть одна и та же функция фигуры, иначе анимация не будет плавной. Для polygon() и shape() в каждом ключевом кадре нужно одно и то же число точек.

В примере clip-path элемента переходит между пятиугольником и звездой, заданными функцией polygon(). Правило заливки evenodd показывает, как движущиеся точки образуют пересекающиеся области.

Анимация с offset-path

Элементы можно двигать вдоль контуров, которые строят эти функции. Свойство offset-path задаёт фигуру-путь, а offset-distance — положение на этом пути. С функцией ray() и свойством offset-path элемент движется по прямой.

В примере один и тот же многоугольник используется и как clip-path, и как offset-path. Анимация через offset-distance ведёт меньшие звёзды по тому же многоугольнику, который большая звезда использует как clip-path.

Источник — Paths, shapes, clipping, and masking

Комментарии