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. Все примеры в посте — живые демо, не скриншоты.
SVG clip-path для SVG элементов
clipPath
позволяет использовать векторную маску любой формы.
Применение SVG clipPath
для HTML-элементов поддерживается только в Firefox, но если перенести разметку в SVG — маска заработает во всех современных браузерах.
Фигуры внутри clipPath
можно комбинировать, чтобы получить более сложные, также в clipPath
может использоваться текст. Текст, использующийся в clipPath
, нельзя выделить и скопировать, при этом текст обрезаемого элемента — можно, правда, копируется он как-то странно.
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
точно также работают для фигур.
При использовании заливки для текста, текст всё ещё остается текстом: его можно выделить и скопировать (попробуйте на примерах ниже), ему можно задать размер и шрифт.
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>
...
</text>
</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, перспективы открываются очень интересные.