Шаблоны

Шаблоны (Patterns)

Patterns, по моему мнению, одни из самых запутанных типов заполнения (fill types) в SVG. Несмотря на это ,они являются очень мощным инструментом, так что о них определённо стоит поговорить и понять хотя бы основные части. Как и градиенты, элементы <pattern> должны быть помещены в секцию <defs> в вашем SVG-файле.

html
<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>
ScreenshotLive sample

Внутри элемента <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 , мы можем поместить все элементы в одну единую систему:

xml
 <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, чтобы они не меняли форму, если объект:

xml
 <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 пикселей, но я должен отметить его не исчерпывающее изображение, и есть другие варианты, доступные в зависимости от вашего приложения.