SVG-паттерны

Содержание:
patternUnits
patternContentUnits
patternTransform
x, y
width, height
xlink:href
Примеры кода
Примеры паттернов
Анимация
Варианты использования

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

Самый простой пример кода выглядит вот так:

<pattern id="pattern"
  width="100%" height="100%">
  ...
</pattern>

Помимо id, без которого не получится применить паттерн к элементу, обязательно нужно указывать ширину и высоту паттерна, потому что по умолчанию их значения равны нулю (и паттерн не отобразится).

Добавим в паттерн какое-нибудь содержимое, например, вот такой кружок:

<pattern id="pattern"
  width="100%" height="100%">
  <circle r="30%" cx="50%" cy="50%"
    fill="crimson"/>
</pattern>

Сам по себе паттерн не отображается на странице. Чтобы увидеть паттерн в действии, его нужно применить к фигуре, это можно сделать, например, с помощью атрибута fill:

<polygon fill="url(#pattern)"
  points="85,0 170,61 137,161 32,161 0,61"/> 

Либо то же самое через CSS:

polygon {
  fill: url(#pattern);
}

Результат:

See the Pen WoNQpZ by yoksel (@yoksel) on CodePen.

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

Мы видим, что круг с координатами центра 50% 50% находится посередине SVG-изображения, а не посередине фигуры с паттерном, также очевидно, что радиус 30% рассчитывается относительно всего изображения, а не относительно фигуры, которой задана заливка. Такое поведение не всегда будет желаемым, и его можно менять с помощью атрибутов, которые будут описаны ниже.

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

See the Pen ObJOLM by yoksel (@yoksel) on CodePen.

Для более сложных случаев нужно как следует разобраться с остальными свойствами паттерна.

patternUnits

Этот атрибут задаёт систему координат для внешних размеров паттерна, то есть влияет на атрибуты x, y, width и height.

Возможные значения:

userSpaceOnUse — используется система координат родительского SVG-элемента. Если выбрано это значение, размеры и координаты можно задавать как в процентах, так и в единицах текущей системы координат; objectBoundingBox — используется система координат элемента, к которому применяется паттерн. Если выбрано это значение, размеры и координаты можно задавать в процентах или в значениях от 0.0 до 1.0.

По умолчанию patternUnits использует значение objectBoundingBox.

See the Pen patternUnits and pattern sizes by yoksel (@yoksel) on CodePen.

patternContentUnits

patternContentUnits определяет систему координат для содержимого паттерна. Важно: если паттерну задан viewBox, этот атрибут работать не будет.

Возможные значения:

userSpaceOnUse — используется система координат родительского SVG-элемента. Если выбрано это значение, размеры и координаты можно задавать как в процентах, так и в единицах текущей системы координат; objectBoundingBox — используется система координат элемента, к которому применяется паттерн. Если выбрано это значение, размеры и координаты можно задавать только в числах от 0.0 до 1.0. Процентные значения здесь использовать не получится, потому что они очень странно себя ведут (подробнее можно почитать тут).

По умолчанию patternContentUnits использует значение userSpaceOnUse.

See the Pen patternContentUnits and pattern content by yoksel (@yoksel) on CodePen.

patternTransform

Атрибут позволяет добавить трансформацию паттерну.

По умолчанию в SVG центр трансформации (transform-origin) находится в левом верхнем углу SVG-элемента. Для вращения (rotate) можно вместе с углом поворота задать и центр трансформации, это выглядит примерно так:

patternTransform="rotate(45, 250, 150)"

Первое число — угол поворота, второе и третье — координаты центра вращения.

В этом коде координаты заданы в единицах текущей системы координат, это будет работать с patternUnits="userSpaceOnUse". При использовании patternUnits="objectBoundingBox" координаты нужно задавать в относительных значениях в диапазоне от 0.0 до 1.0, код трансформации в этом случае должен быть таким:

patternTransform="rotate(45, .5, .5)"

Это должно работать, но не работает, как можно увидеть в демо ниже (потаскайте ползунок):

See the Pen patternTransform (and bug with transform-origin) by yoksel (@yoksel) on CodePen.

Эксперименты показали, что координаты центра трансформации в этом случае, как и при patternUnits="userSpaceOnUse", могут быть заданы в единицах текущей системы координат. Это будет работать, хотя и не должно.

Учитывая такое странное поведение, я бы рекомендовала не использовать patternTransform вместе с patternUnits="objectBoundingBox".

x, y

Атрибуты позволяют задать положение плитки паттерна относительно верхнего левого угла:

See the Pen patternUnits and pattern coordinates (x & y) by yoksel (@yoksel) on CodePen.

width, height

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

See the Pen patternUnits and pattern coordinates (width & height, no viewBox) by yoksel (@yoksel) on CodePen.

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

See the Pen patternUnits and pattern coordinates (width & height, has viewBox) by yoksel (@yoksel) on CodePen.

Если содержимое должно заполнять плитку целиком, пусть и с искажениями, нужно указать, что сохранять пропорции не требуется, для этого нужно добавить preserveAspectRatio="none":

See the Pen patternUnits and pattern coordinates (width & height, has viewBox + preserveAspectRatio="none") by yoksel (@yoksel) on CodePen.

Напомню, что по умолчанию ширина и высота паттерна равны нулю, поэтому размеры обязательно нужно указывать явным образом, иначе паттерн не отобразится.

viewBox, preserveAspectRatio

Если вы с ними не знакомы, в этом разделе будет немного вводной информации.

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

Содержимое SVG-изображения отрисовывается на бесконечном холсте, и по умолчанию видимая область совпадает с размерами самого SVG-элемента. Если задан viewBox, уже он будет задавать размеры и координаты прямоугольника, определяющего видимую область. Если размеры видимой области, заданные во viewBox, не совпадают с размерами SVG-элемента, видимая область растянется или сожмётся, чтобы вместиться целиком. Также этот атрибут определяет соотношение сторон, которое должно сохраняться при изменении размеров элемента. Подробнее про viewBox можно почитать в спецификации.

preserveAspectRatio отвечает за поведение содержимого при изменении размеров родительского элемента, у него для этого есть большой набор разных значений. Значение по умолчанию — xMidYMid meet, то есть содержимое должно помещаться целиком с сохранением пропорций (meet) и выравниваться по центру (xMidYMid). Схожим образом можно управлять поведением фоновых изображений с помощью background-size или поведением одного элемента внутри в другого с помощью object-fit. Важно помнить, что preserveAspectRatio не работает без viewBox. Узнать больше о значениях preserveAspectRatio можно в спецификации.

При наличии viewBox при ресайзе элемент стремится поместиться в заданную область целиком с сохранением пропорций. Если сохранять пропорции не нужно, задаётся атрибут preserveAspectRatio со значением none.

Как и некоторые другие SVG-элементы, паттерны могут наследовать свойства друг друга. Ссылка на паттерн, содержимое и свойства которого нужно унаследовать, задаётся в атрибуте xlink:href.

Из паттерна по ссылке унаследуются все свойства и содержимое, которые не переопределены в текущем паттерне. В примере ниже правый паттерн наследует из левого содержимое и трансформацию:

See the Pen patternTransform and xlink:href by yoksel (@yoksel) on CodePen.

Примеры кода

Весь этот набор атрибутов не только позволяет гибко управлять паттерном, но и немного взрывает мозг при попытке их использовать.

По моему опыту, самый удобный код получается при одновременном использовании viewBox, width и height. viewBox определяет размер видимой области до применения какого-либо масштабирования, то есть какую часть содержимого паттерна нужно использовать как плитку — это немного похоже на применение инструмента «Crop» в фотошопе. Затем, указав ширину и высоту, можно управлять размером полученной плитки. То есть изначально плитка может быть любого размера, а нужный размер можно настроить потом.

Важный момент: чтобы между плитками не было пустых мест, нужно в width и height сохранять пропорции, указанные во viewBox. Значения размеров при этом не должны зависеть от размеров фигуры или SVG-элемента, следовательно, нужно добавить ещё один необходимый атрибут — patternUnits со значением userSpaceOnUse (то есть нужно использовать систему координат всего SVG-элемента).

Пример кода:

<pattern id="pattern"
   patternUnits="userSpaceOnUse"
   viewBox="0 0 275 175"
   width="150" height="95">
   ...
</pattern>

Результат:

See the Pen patternUnits and pattern coordinates (width & height, has viewBox) by yoksel (@yoksel) on CodePen.

Изменяем размеры паттерна, он послушно масштабируется. При этом размеры фигуры на паттерн не влияют:

See the Pen width, height & viewBox + resize shape by yoksel (@yoksel) on CodePen.

Для своих экспериментов я обычно выбираю этот вариант, с ним удобнее работать.

А что если паттерн должен реагировать на изменение размеров элемента?

Вариант 1. Нужно задать атрибуту patternContentUnits значение objectBoundingBox. То есть внутри паттерна система координат должна строиться относительно элемента, к которому применён паттерн.

Пример кода:

<pattern id="pattern"
   patternContentUnits="objectBoundingBox"
   width="50%" height="50%">
   ...
</pattern>

Напомню, что при patternContentUnits="objectBoundingBox" размеры и координаты внутри паттерна можно задавать только в числах от 0.0 до 1.0, проценты работать не будут. Так как все размеры содержимого задаются относительно размеров фигуры, тянуться они так же будут вместе с ней, без сохранения пропорций. Это видно на демо:

See the Pen width, height & viewBox + resize shape by yoksel (@yoksel) on CodePen.

Вариант 2. Он проще и удобнее, потому что содержимое паттерна сохраняет свою систему координат и единицы измерения. При этом способе patternUnits не задаётся, потому что используется значение по умолчанию: objectBoundingBox. Размеры плитки паттерна задаются в процентах или в значениях от 0.0 до 1.0, они будут зависеть от размеров фигуры. Чтобы содержимое паттерна ресайзилось вместе с ним, нужно добавить viewBox, а чтобы оно при этом заполняло всю плитку, хоть и с искажениием пропорций, — preserveAspectRatio="none":

<pattern id="pattern"
   width="75%" height="75%"
   viewBox="0 0 275 175"
   preserveAspectRatio="none"
   >
   ...
</pattern>

Результат:

See the Pen width, height & viewBox + preserveAspectRatio by yoksel (@yoksel) on CodePen.

Таким образом можно с минимальными усилиями сделать резиновый паттерн.

Примеры паттернов

Паттерн с растровой картинкой:

See the Pen eGAxK by yoksel (@yoksel) on CodePen.

Паттерн с векторным содержимым:

See the Pen SVG pattern by yoksel (@yoksel) on CodePen.

Паттерн с текстовым содержимым:

See the Pen WopeZb by yoksel (@yoksel) on CodePen.

Как видите, с помощью паттернов можно залить текст текстом : )

Все мои демо с паттернами собраны в этой коллекции.

Анимация

Содержимое паттернов можно анимировать с помощью CSS и Js. Анимации, сделанные с помощью Javascript, будут работать везде (демо), у CSS-анимаций могут быть проблемы с поддержкой браузерами.

В Firefox внутри паттерна не работают CSS-трансформации (вот демо, на котором можно потестить), — не работают и не анимируются, хотя они без проблем работают в Хроме. В некоторых случаях изменение размера фигуры можно имитировать управляя толщиной обводки. CSS-анимация заливки и обводки работает почти во всех браузерах, кроме IE (в IE11 не работает, в Edge не смотрела).

Примеры можно посмотреть в той же коллекции, многие демки содержат анимации.

Варианты использования

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

See the Pen Animated SVG pattern with GSAP by yoksel (@yoksel) on CodePen.

При этом сам паттерн, как мы видели в одном из предыдущих демо, тоже может содержать текст.

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

С паттернами можно сделать много интересного, и, несмотря на некоторые сложности в использовании, они определённо стоят того, чтобы в них разобраться.

PS: Честно говоря, даже несмотря на относительно давнее знакомство с паттернами, они всё ещё вызывают у меня трудности. Тем не менее, если у вас возникнут вопросы, я постараюсь на них ответить.

Ссылки по теме:
Patterns
Коллекция демо с SVG-паттернами
Если вы нашли ошибку или неточность, вы можете отредактировать статью с помощью prose.io, а также можно написать мне в комментариях или в Twitter.
Система комментирования от Disqus