Шаблоны
Шаблоны (Patterns)
Patterns, по моему мнению, одни из самых запутанных типов заполнения (fill types) в SVG. Несмотря на это ,они являются очень мощным инструментом, так что о них определённо стоит поговорить и понять хотя бы основные части. Как и градиенты, элементы <pattern>
должны быть помещены в секцию <defs> в вашем SVG-файле.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white" />
<stop offset="95%" stop-color="blue" />
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red" />
<stop offset="95%" stop-color="orange" />
</linearGradient>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<rect x="0" y="0" width="50" height="50" fill="skyblue" />
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)" />
<circle
cx="25"
cy="25"
r="20"
fill="url(#Gradient1)"
fill-opacity="0.5" />
</pattern>
</defs>
<rect fill="url(#Pattern)" stroke="black" width="200" height="200" />
</svg>
Внутри элемента <pattern> вы можете использовать любые другие основные фигуры, которые использовались ранее. И каждая из них может быть стилизована, используя любые из возможных вариантов стилизаций, которые вы изучали до этого, включая градиенты и прозрачность. Выше мы просто нарисовали два прямоугольника внутри нашего шаблона (которые перекрываются, и один из которых в два раза больше другого и используется для заполнения всего шаблона), и один круг.
Сбивающая с толку вещь - это определение единиц измерения и размера. В примере выше мы определили ширину и высоту соответствующими атрибутами внутри паттерна, что бы указать как далеко паттерн будет "продолжаться", прежде чем начать повторяться. Также доступны x и y атрибуты для смещения стартовой точки этого прямоугольника в том месте где вы его отрисовываете. Причина по которой они тут использованы, описана ниже.
Как и с gradientUnits
атрибутом, использованном ранее, у паттерна также есть атрибут patternUnits
. Он определяет единицы измерения, которые принимают атрибуты. По дефолту используется значение "objectBoundingBox", (как и в предыдущем разделе). Так что значения от 0 до 1 будут масштабированы в зависимости от размеров объекта, к которому вы применяете паттерн. Поскольку в данном случае мы хотим, чтобы шаблон повторялся 4 раза по горизонтали и вертикали, ширину и высоту мы установили в значение 0.25. Что означает 0.25 от размера целевого объекта
В отличие от градиентов, у паттернов есть 2ой атрибут - patternContentUnits
, который описывает единицы измерения, используемые в базовых фигурах внутри элемента pattern. Дефолтное значение для этого атрибута - userSpaceOnUse, противоположность атрибуту patternUnits
. Это означает, что если вы не укажете один или оба этих атрибута (patternContentUnits
и/или patternUnits
), фигуры, которые вы будете рисовать внутри блока <pattern>, будут отрисованы в другой системе координат (отличной от той, которую использует паттерн). Это может немного запутывать, если вы пишете код вручную.
Чтобы сделать эту работу в приведённом выше примере, нам пришлось рассмотреть размер нашей коробки (200 пикселей) и тот факт, что мы хотели, чтобы рисунок повторялся 4 раза по горизонтали и по вертикали. Это означает, что каждый блок шаблонов был квадратом 50 × 50. Затем два прямоугольника и круг внутри рисунка были рассчитаны на размер в коробке 50x50. Все, что мы нарисовали вне этой коробки, не было бы показано. Шаблон также должен был быть смещён на 10 пикселей, чтобы он начинался в верхнем левом углу нашего окна, поэтому атрибуты x и y шаблона должны были быть скорректированы до 10/200 = 0,05.
Здесь предостережение заключается в том, что если объект изменяет размер, сам шаблон будет масштабироваться, чтобы соответствовать ему, но объекты внутри не будут. Таким образом, хотя у нас все ещё будет 4 повторяющихся блока внутри шаблона, объекты, составляющие этот шаблон, будут оставаться одного и того же размера, и вы окажетесь в больших областях между ними.
Изменяя атрибут patternContentUnits
, мы можем поместить все элементы в одну единую систему:
<pattern id="Pattern" width=".25" height=".25" patternContentUnits="objectBoundingBox">
<rect x="0" y="0" width=".25" height=".25" fill="skyblue"/>
<rect x="0" y="0" width=".125" height=".125" fill="url(#Gradient2)"/>
<circle cx=".125" cy=".125" r=".1" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
Теперь, поскольку содержимое шаблона находится в той же единичной системе, что и шаблон, нам не нужно смещать поле так, чтобы шаблон начинался в правильном месте, и если размер объекта был изменён на более крупный, шаблон автоматически масштабируется так, чтобы в нем было одинаковое количество объектов, повторяющихся внутри него. Это контрастирует с системой userSpaceOnUse, где, если объект изменяет размер, шаблон останется прежним и просто повторится больше раз, чтобы заполнить поле.
Как ни удивительно, но в кругах Gecko, похоже, есть проблемы с рисованием, если их радиус установлен на что-то меньшее 0.075 (хотя их нужно масштабировать, чтобы иметь гораздо больший радиус, чем этот. Это может быть ошибкой только в шаблоне , Или вообще не ошибка, я не уверен). Чтобы обойти это, вероятно, лучше всего избегать рисования блоков «objectBoundingBox», если вам это не нужно.
Ни одно из этих применений не является тем, о чем обычно думают, когда вы думаете о шаблоне. Шаблоны обычно имеют заданный размер и повторяются независимо от формы объекта. Чтобы создать что-то подобное - шаблон и его содержимое должны быть нарисованы в текущем userSpace, чтобы они не меняли форму, если объект:
<pattern id="Pattern" x="10" y="10" width="50" height="50" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
Конечно, это означает, что шаблон не будет масштабироваться, если вы позже измените размер своего объекта. Все три из приведённых выше примеров показаны ниже на прямоугольнике, который слегка удлинён до высоты 300 пикселей, но я должен отметить его не исчерпывающее изображение, и есть другие варианты, доступные в зависимости от вашего приложения.