CSS и SVG маски

Разбираясь с возможностями оформления SVG, заинтересовалась темой масок.

Всё началось вот с этого эксперимента:

See the Pen SVG gradients for text by yoksel (@yoksel) on CodePen.

Для надписи подключен гуглошрифт, заливка сделана паттерном с градиентами, при этом надпись не перестала быть текстом — её можно выделить и скопировать!

Тут следует отметить, что гораздо проще было бы в качестве заливки использовать просто градиент, не заворачивая его в паттерн, но для этого нужно было задать градиенту свойство spreadMethod="repeat", чтобы он повторялся, а в Firefox в этом случае градиент почему-то ломался.

Я посмотрела на поддержку этого демо в разных браузерах, вспомнила про -webkit-background-clip: text, который поддерживается только в вебкитах, и решила посмотреть что ещё есть интересного в этом направлении.

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

Используемые технологии:

  • CSS — маска создается в CSS и применяется к HTML-элементам;
  • SVG + CSS — SVG-маска применяется к HTML-элементам с помощью CSS;
  • SVG — SVG-маска применяется к SVG-элементам. С помощью CSS, но главное здесь то, что и маска, и маскируемый элемент являются SVG.

Лучшая поддержка браузерами — у масок, сделанных полностью на SVG.

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

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

Маски бывают разных типов: одни позволяют обрезать элемент по векторной фигуре (пути) — clip, clip-path, другие ориентируются на яркость цветов или альфа-канал изображения — mask и mask-image — эти способы позволяют получить маску с размытыми краями. Использовать текст в качестве маски можно в SVG clip-path, SVG mask, -webkit-background-clip и SVG fill. Последнее не является маской в прямом смысле слова, но результат выглядит как -webkit-background-clip, и при этом поддерживается всеми современными браузерами.

Все маски вы можете найти на демо-странице, я же расскажу только про самые хорошо поддерживаемые — конечно же, все они на SVG. Все примеры в посте — живые демо, не скриншоты.

Text Nam tristique vestibulum nulla nec accumsan. Nullam commodo eget dolor et ultricies. Nulla ligula elit, placerat a sapien vel, eleifend tincidunt nibh. Suspendisse porta fermentum dictum. Cras eget Maecenas tempus elit quis sapien tempus, sit amet viverra neque mattis. Quisque a tincidunt mi. Proin at justo eu ipsum posuere dignissim vel quis eros. Etiam at libero commodo, varius justo quis, interdum erat. Fusce viverra mollis est, nec euismod ante mattis ut. Aenean eu mauris viverra enim vulputate bibendum. Integer velit metus, cursus et commodo sit amet, gravida vitae purus. Sed scelerisque at odio non condimentum. Sed neque lacus, rhoncus vitae sem ut, pharetra vestibulum elit. Cras ultricies aliquam dolor quis ultrices. Cras eget mauris tincidunt, aliquam erat quis, faucibus augue.

SVG clip-path для SVG элементов

Спецификация

clipPath позволяет использовать векторную маску любой формы. Применение SVG clipPath для HTML-элементов поддерживается только в Firefox, но если перенести разметку в SVG — маска заработает во всех современных браузерах.

Фигуры внутри clipPath можно комбинировать, чтобы получить более сложные, также в clipPath может использоваться текст. Текст, использующийся в clipPath, нельзя выделить и скопировать, при этом текст обрезаемого элемента — можно, правда, копируется он как-то странно.

Nam tristique vestibulum nulla nec accumsan. Nullam commodo eget dolor et ultricies. Nulla ligula elit, placerat a sapien vel, eleifend tincidunt nibh. Suspendisse porta fermentum dictum. Cras eget adipiscing magna. Nunc massa justo, placerat at porta at, mollis nec eros. Nullam eu justo erat. Curabitur eget rhoncus purus, interdum posuere ligula. Ut ultricies fermentum dignissim. Aliquam et arcu tempus, euismod nisi eu, lobortis est. Integer ultrices aliquet enim eu ultrices. Nam tristique vestibulum nulla nec accumsan. Nullam commodo eget dolor et ultricies. Nulla ligula elit, placerat a sapien vel, eleifend tincidunt nibh. Suspendisse porta fermentum dictum. Cras eget adipiscing magna. Nunc massa justo, placerat at porta at, mollis nec eros. Nullam eu justo erat. Curabitur eget rhoncus purus, interdum posuere ligula. Ut ultricies fermentum dignissim. Aliquam et arcu tempus, euismod nisi eu, lobortis est. Integer ultrices aliquet enim eu ultrices.

SVG:

<!-- Маска-путь -->
<clipPath id="clipping">
  <polygon points="98.4999978 153.75..."/>
</clipPath>

<!-- Маска-текст -->
<clipPath id="clipping-text">
  <text x="0" y=".88em">Text</text>
</clipPath>

CSS:

/* Для текстовой маски можно задать параметры шрифта */
#clipping-text {
  font: bold italic 15em/1 Georgia;
  }

/* Применение маски */
.item {
  clip-path: url(#clipping);
  }

SVG mask для SVG элементов

Спецификация

SVG mask позволяет задать элемент или сочетание элементов, которые будут работать маской по альфа-каналу или по яркости. SVG mask для HTML-элементов также поддерживается только в Firefox, но при переносе разметки в SVG работает везде.

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

SVG mask (в отличие от clipPath) позволяет сделать маску с размытыми краями.

SVG:

<!-- Маска с сочетанием градиентов и фигур -->
<mask id="masking" maskUnits="objectBoundingBox">
    <!-- Фигура с полосатым градиентом -->
    <rect y="0" width="1" height="1" fill="url(#gradient)" />
    <!-- Круги -->
    <circle cx=".5" cy=".5" r=".4" fill="gray" />
    <circle cx=".5" cy=".5" r=".3" fill="white" />
    ...
</mask>

<!-- Маска из картинки -->
<mask id="masking-lum" maskUnits="objectBoundingBox"
      x="0" y="0">
    <image xlink:href="АДРЕС КАРТИНКИ" width="200" height="300" />
</mask>

CSS:

.item {
    mask: url(#masking);
    }

SVG fill

Спецификация

fill — заливка, а не маска, но позволяет довольно неплохо имитировать маски. Заливкой может цвет, градиент или паттерн.

Паттерн (pattern) — это элемент SVG. В него можно положить всё что угодно, чтобы потом использовать содержимое контейнера как повторяющийся фон. В нем можно создать узор из других элементов SVG, а также завернуть в него картинку или текст.

Правда, с текстом есть некоторые трудности: в SVG текст не умеет переноситься сам по себе. Чтобы сделать текст с переносами, как в обычном HTML, его нужно завернуть в foreignObject, но заливка с таким паттерном не работает, хотя в спеке для паттерна написано, что foreignObject в паттерн вставлять можно.

Так что, чтобы сделать заливку текстом, надо вставить его через тег text, а каждую строку завернуть в tspan и задать ей нужное положение. Способ громоздкий, но работает.

Используя в качестве паттерна картинки, можно получить надписи с любой текстурой, которую подскажет ваша фантазия. При этом способ сработает во всех современных браузерах, в отличе от -webkit-background-clip: text, так что фоллбеков потребуется сильно меньше.

Собственно, текст без заливки, но с текстурной обводкой тоже можно сделать: в этом случае паттерн следует задать свойству stroke.

Примеры сделаны для текста, но fill и stroke точно также работают для фигур.

При использовании заливки для текста, текст всё ещё остается текстом: его можно выделить и скопировать (попробуйте на примерах ниже), ему можно задать размер и шрифт.

Text Text Text

SVG:

<!-- Паттерн с картинкой -->
<pattern id="pattern" patternUnits="userSpaceOnUse"
         width="200" height="300" viewbox="0 0 200 300">
    <image xlink:href="YOUR IMAGE" width="200" height="300" />
</pattern>

<!-- Паттерн с текстом -->
<pattern id="pattern-text" patternUnits="userSpaceOnUse"
         width="450" height="95">
    <text x="0" y="0">
        <tspan x="0" y="1em">Nam tristique vestibulum
            nulla nec accumsan...
        </tspan>
        ...
</pattern>

CSS:

/* Заливка с паттерном */
text {
    fill: url(#pattern);
    }
/* Обводка с паттерном */
text {
    stroke: url(#pattern);
    fill: none;
    }

Важное уточнение: в Firefox и старой Опере ссылки вида mask: url(#masking); не работают, если страница с SVG градиентами, паттернами и масками находится в одной директории, а CSS — в другой. Это лечится полным указанием пути от корня сайта, например mask: url(/page-url/#masking);, чтобы браузеры знали где именно искать элемент с таким ID. За указание на проблему спасибо legomushroom.

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

Ссылки по теме:
CSS Masking
CSS Masks – How To Use Masking In CSS Now
Using Masks
Techniques for Creating Textured Text
Text masking — The standards way
Если вы нашли ошибку или неточность, вы можете отредактировать статью с помощью prose.io, а также можно написать мне в комментариях или в Twitter.
Система комментирования от Disqus