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

Дирк Шульц

Оригинал: http://www.html5rocks.com/en/tutorials/masking/adobe/

Перевод: Влад Мержевич

В компьютерной графике отсечение и маскирование — это две наиболее используемые операции. Обе они визуально скрывают часть элемента. Если вы ранее работали с SVG или HTML Canvas, то эти операции для вас, скорее всего, уже не новы. Отсечение определяет область элемента которая будет видна, всё остальное за пределами этой области не отображается и получается «отрезанным». При маскировании изображение маски объединяется с элементом, влияя на его альфа-канал. Части маскированного элемента получаются полностью или частично прозрачными. Новая спецификация CSS Masking направлена на объединение этих двух операций в мире HTML.

Отсечение в CSS 2.1

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

CSS:

img {
  position: absolute;
  clip: rect(10px, 290px, 190px, 10px);
}

HTML:

<img src="image.jpg" width="568">

Свойство clip также ограничено отдельными элементами SVG. Это одна из причин, почему в спецификацию SVG добавлено свойство clip-path пригодное для маскирования в CSS.

Свойство clip-path

Свойство clip-path может применяться ко всем элементам HTML, графическим элементам и контейнерам SVG. Свойство либо ссылается на элемент <clipPath>, либо на одну из базовых фигур представленных в CSS Exclusions.

Элемент <clipPath> берёт любой графический элемент из SVG и использует его в качестве области отсечения. Графическими элементами в SVG являются <rect>, <circle>, <ellipse>, <path>, <polygon><image> и <text>. <clipPath> также позволяет комбинировать несколько графических элементов. Объединение всех фигур затем используется как область отсечения. Следующий пример демонстрирует использование <clipPath>:

CSS:

img {
  clip-path: url(#clipping);
}

HTML:

<svg>
  <defs>
    <clipPath id="clipping">
      <circle cx="284" cy="213" r="213" />
    </clipPath>
  </defs>
</svg>

<img src="image.jpg" width="568">

Базовые фигуры с другой стороны не требуют какой-либо разметки SVG. Они были добавлены к clip-path, чтобы предоставить удобные универсальные функции для простых операций отрезания.

  • inset(<top> <right> <bottom> <left> [ round <border-radius> ]?) определяет прямоугольник похожий на фунцию rect() у свойства clip. В параметрах указывается смещение верхней, правой, нижней и левой сторон. Функция также имеет необязательный радиус скругления с синтаксисом как у свойства border-radius.
  • circle(<r>? [ at <position> ]?) определяет простой круг с необязательным радиусом. Кроме того, необязательный параметр положения задаёт центральную точку окружности. <position> имеет тот же синтаксис, что и свойство background-position.
  • ellipse(<rx> <ry>? [ at <position> ]?) определяет эллипс с горизонтальным и необязательным вертикальным радиусом, а также центральной точки на основе синтаксиса свойства background-position.
  • polygon(<x1> <y1>, <x2> <y2>, ..., <xn> <yn>) определяет многоугольник основываясь на списке координат.

Ключевые слова вроде content-box, border-box, margin-box могут использоваться в сочетании с базовыми фигурами для изменения положения и размера указанного пути отсечения. Если ключевые слова применяются без базовых фигур, то они сами действуют как пути отсечения с учётом свойства border-radius. CSS разметка может выглядеть как в следующем примере:

img {
  clip-path: polygon(0px 208px, 146.5px 207px, 147px 141.2px, ...);
}

Отсечение может быть весьма полезно для представления визуального контента. В следующих примерах к изображениям применяются различные операции отсечения.

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

Прокрутите список для просмотра эффекта.

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

Анимация clip-path

Базовые фигуры и содержимое элемента <mask> может быть анимировано. Следующий пример показывает анимацию фигуры в виде звезды.

Вот исходный код для анимации базовой фигуры:

img:hover {
  clip-path: polygon(0px 208px, 146.5px 207px, 147px 141.2px, ...);
  animate: star 3s;
}
@keyframes star {
  0% {
    clip-path: polygon(0px 208px, 146.5px 207px, 147px 141.2px, ...);
  },
  100% {
    clip-path: polygon(0px 208px, 146.5px 207px, 147px 141.2px, ...);
  }
}

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

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

Маска по яркости

В маске по яркости вначале изображение маски преобразуется в чёрно-белое изображение (если оно ещё не такое). «Светлые» части маски сильнее маскируют элемент в том же месте. К примеру, чёрный цвет означает полную прозрачность, белый полную непрозрачность, а серые оттенки означают частичную прозрачность элемента.

Альфа-маска

Альфа-маска использует тот же принцип, что и маска по яркости. Разница между ними только в альфа-канале изображения. Чем меньше уровень прозрачности маски, тем больше виден элемент в той же точке.

Подведем итог: оба типа маскирования влияет на уровень прозрачности элемента. Изображение ниже является результатом обоих маскирующих операций показанных выше.

Спецификация CSS Masking описывает два универсальных свойства для маскирования: mask и mask-border.

Свойство mask

Свойство mask сочетает в себе изображение маски и ссылку на маску.

Первый способ заключается в использовании свойств mask-imagemask-repeatmask-positionmask-clipmask-origin и mask-size, которые определяются подобно частям background вроде background-image. Как и для background-image можно определить несколько исходников маски, каждый из них представляет собой изображение описанное в CSS3 Images. Все исходники маски будут объединены в единое изображение маски, далее оно используется чтобы замаскировать элемент и его содержимое, как описано выше. Изображение может быть в любом растровом формате вроде JPG или PNG, а также SVG или градиентом CSS. Приведённый выше пример с маской может быть просто реализован с помощью следующего кода:

img {
  mask-image: url(mask.svg);
}

Если исходник маски должен быть растянут до размера содержимого, то просто используйте универсальное свойство mask как для фона, словно вы имеете дело со свойством background.

img {
  mask: url(mask.svg) top left / cover;
}

Вторым способом является ссылка на элемент <mask>, который описан в SVG 1.1. Элемент <mask> берёт любой графический элемент, а также группу элементов из SVG и использует их для создания изображения маски.

CSS:

img {
  mask: url(#masking);
}

HTML:

<svg>
  <defs>
    <linearGradient id="gradient" x1="0" y1="00%" x2 ="0" y2="100%">
      <stop stop-color="black" offset="0"/>
      <stop stop-color="white" offset="1"/>
    </linearGradient>
    <mask id="masking" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
      <rect y="0.3" width="1" height=".7" fill="url(#gradient)" />
      <circle cx=".5" cy=".5" r=".35" fill="white" />
    </mask>
  </defs>
</svg>
<img src="image.jpg" width="568">

В итоге это выглядит как такое изображение.

Свойство mask-border

Свойство mask-border позволяет разделить изображение маски на 9 фрагментов: четыре уголка, четыре края и средняя часть. Эти части могут разрезаться, масштабироваться и растягиваться разными способами в соответствии с размером маски. Свойство заимствует функциональность из border-image и обеспечивает эффективное маскирование краёв и углов содержимого. Следующий пример демонстрирует поведение свойства:

img {
  -webkit-mask-box-image: url("stamp.svg") 35 repeat;
  mask-border: url("stamp.svg") 35 repeat;
}

Следующее изображение применяется в качестве изображения маски и разделено на девять частей:

Pattern of a stamp to be used as mask

Эти части в данный момент используется для маскирования углов и краёв содержимого, в результате мы получим следующий вид:

Поддержка браузерами

Интересным моментом является поддержка в браузерах. Когда дело доходит до конкретной реализации всё становится довольно сложным.

Все браузеры поддерживают clip согласно спецификации. Все браузеры поддерживают свойства mask и clip-path для элементов SVG согласно спецификации  SVG 1.1. Но только один браузер позволяет применять эти свойства для HTML-элементов: Firefox (вроде как). Рассмотрим подробнее.

Свойства clip-path и mask со ссылкой на <clipPath>, а также элемент <mask> работают в Firefox изначально без всяких префиксов. С другой стороны, mask-image, mask-border и связанные с ними свойства не поддерживаются вообще. Базовые фигуры для отсечения тоже не работают.

Blink и браузеры основанные на WebKit вроде Chrome и Safari поддерживают mask-image и mask-border (только они называются -webkit-mask-box-image в обоих случаях) и связанные с ними свойства. Все они пишутся с префиксами и могут применяться к элементам HTML. Ночные сборки обоих браузеров уже поддерживают свойство -webkit-clip-path для базовых фигур и ссылки на элемент <clipPath>. WebKit дополнительно поддерживает ключевое слово box-sizing.

Если вы хотите попробовать отсечение и маскирование, то используйте свойства как с префиксом, так и без него. Свойство без префикса должно ссылаться на <mask> или <clipPath> в настоящее время.

<style>
#image {
  mask: url(#mask);
  -webkit-mask: url(mask.svg) top left / cover;
}
</style>
<img id="image" src="coolImage.jpg" width="400">
<svg width="0" height="0">
  <mask id="mask">
    ...
  </mask>
</svg>

Будьте осторожны, другие браузеры ещё не понимают маскирование и отсечение для элементов HTML.

CSS по теме

Не выкладывайте свой код напрямую в комментариях, он отображается некорректно. Воспользуйтесь сервисом cssdeck.com или jsfiddle.net, сохраните код и в комментариях дайте на него ссылку. Так и результат сразу увидят.