[Перевод] SVG фильтры 101
Это первая статья в серии об SVG фильтрах. Это руководство поможет понять, что это такое, и покажет, как использовать их для создания собственных визуальных эффектов.
CSS в настоящее время предоставляет нам способ применения цветовых эффектов к изображениям, таких как насыщенность, яркость и контрастность, среди других эффектов, с помощью свойства filter и его функций, поставляющихся с ним.
Теперь у нас есть 11 функций фильтра в CSS, которые выполняют ряд эффектов от размытия до изменения цветовой контрастности и насыщенности и многое другое. Подробную информацию об этом можно посмотреть в справочнике CSS.
Хотя мощные и очень удобные, CSS фильтры также очень ограничены. Эффекты, которые мы можем создать с их помощью, часто применимы к изображениям и ограничиваются цветовыми манипуляциями и простым размытием. Таким образом, для создания более мощных эффектов, которые мы можем применить к более широкому ряду элементов, нам понадобится более широкий ассортимент функций. Эти функции доступны сегодня, и доступны уже более десяти лет в SVG. В этой статье, которая является первой в серии о SVG-фильтрах, вы узнаете о функциях SVG-фильтров, известных как «примитивы», и о том, как их использовать.
CSS-фильтры импортированы из SVG. Они являются достаточно хорошо оптимизированными версиями подмножества эффектов фильтрации, представленных в SVG и существующих в спецификации SVG уже в течение многих лет.
В SVG существует больше эффектов фильтрации, чем в CSS, а SVG-версии более мощные и выполняют гораздо более сложные эффекты, чем их CSS-ярлыки. Например, в настоящее время можно размыть элемент, используя CSS-функцию фильтрации blur (). Применение эффекта размытия с помощью этой функции создаст равномерное размытие по Гауссу элемента, к которому оно применяется. На следующем рисунке показан результат применения размытия в 6 px к изображению в CSS:
Рис_1. Эффект применения CSS-функции blur () — размытия по Гауссу.
Функция blur () создает эффект размытия, который равномерно применяется в обоих направлениях (X и Y) на изображении. Но эта функция — просто упрощенное и ограниченное сокращение примитива фильтра размытия, доступного в SVG, который позволяет нам размыть изображение либо равномерно, либо применить однонаправленный эффект размытия вдоль осей X или Y.
Рис_2. Эффект применения SVG-функции blur () раздельно по осям.
SVG-фильтры могут применяться как к HTML-элементам, так и к SVG-элементам. Эффект фильтрации SVG может быть применен к HTML-элементу в CSS с помощью функции фильтра url (). Например, если у вас есть эффект фильтра с идентификатором myAwesomeEffect, определенным в SVG (мы поговорим об определении эффектов фильтров в SVG в ближайшее время), вы можете применить этот эффект к HTML-элементу или изображению вот так:
.el {
filter: url(#myAwesomeEffect);
}
Лучше всего, как вы увидите в этой серии статей, SVG-фильтры способны создавать эффекты уровня Photoshop в браузере, используя несколько строк кода. Я надеюсь, что эта серия поможет развеять ореол тайны и раскрыть часть потенциала SVG фильтров, что вдохновит вас на использование их в собственных проектах.
Но, а как насчет поддержки браузерами, спросите вы…?
Поддержка браузерами
Поддержка браузерами большинства SVG фильтров впечатляет. Однако способ применения эффекта может отличаться у некоторых браузеров в зависимости от поддержки ими отдельных примитивов фильтра, используемых в эффекте SVG-фильтрации, а также от возможных ошибок браузера. Поддержка браузером также может отличаться, когда фильтр SVG применяется к элементам SVG или к элементам HTML.
Я бы рекомендовала вам рассматривать эффекты фильтрации как расширение: вы почти всегда можете применить эффект в качестве улучшения поверх совершенно полезного опыта работы без фильтра. Многие знают, что я поддерживаю прогрессивный подход создания UI, когда это возможно. Таким образом, мы не будем слишком беспокоиться о поддержке браузерами в этой серии статей.
Наконец, несмотря на то, что поддержка фильтров SVG в целом хороша, имейте в виду, что некоторые эффекты, которые мы рассмотрим позже, можно считать экспериментальными. Я буду указывать любые основные проблемы или ошибки, если и когда они будут.
Итак, как определить и создать эффект фильтрации в SVG?
Элемент < filter>
Подобно линейным градиентам, маскам, узорам и другим графическим эффектам в SVG, фильтры имеют удобно-названный специализированный элемент: < filter>.
Он никогда не отображается напрямую, а используется только как объект, на который можно ссылаться с помощью атрибута filter в SVG или функции url () в CSS. Такие элементы, которые не отображаются без явной ссылки, обычно определяются как шаблоны внутри элементов < defs> в SVG. Но SVG < filter> не нужно упаковывать в элемент defs. Обернете ли вы фильтр в элемент defs или нет, он все равно не будет отображаться.
Причина этого заключается в том, что фильтр требует исходное изображение для обработки. А если вы явно не определяете это исходное изображение, вызывая фильтр на нем, у фильтра не будет ничего для визуализации, и поэтому он не сработает.
Очень простой, минимальный пример кода, определяющий фильтр SVG и применяющий его к исходному изображению в SVG, будет выглядеть следующим образом:
Фильтр в приведенном выше примере кода ничего не делает на данный момент, так как он пуст. Чтобы создать эффект фильтра, необходимо определить серию из одной или нескольких операций фильтрации, которые создают этот эффект внутри фильтра. Другими словами, элемент < filter> является контейнером для серии операций фильтрации, которые совместно и создают эффект фильтра. Эти операции фильтрации в SVG называются »примитивами фильтра».
Примитивы фильтра
Таким образом, в SVG каждый элемент < filter> содержит набор примитивов фильтра в качестве дочерних элементов. Каждый примитив фильтра выполняет одну элементарную графическую операцию на одном или нескольких входах, создавая графический результат.
Примитивы фильтра удобно названы в честь графических операций, которые они выполняет. Например, примитив, применяющий эффект размытия Гаусса (Gaussian Blur) к источнику графики, называется feGaussianBlur. Все примитивы имеют одинаковый префикс: fe, сокращенно от »filter effect». Опять же, имена в SVG удобно выбирать, чтобы понимать, что это за элемент или что он делает.
В следующем фрагменте кода показано, как будет выглядеть простой фильтр, если применить к изображению размытие Гаусса размером 5 px:
В настоящее время в спецификации SVG Filter определено 17 примитивов фильтров, которые способны создавать чрезвычайно мощные графические эффекты, включая, но не ограничиваясь, генерацию шума и текстуры, световые эффекты, цветовые манипуляции (от канала к каналу) и многое другое.
Примитив фильтра работает, принимая графический источник на ввод и выводя на другой. А выход одного эффекта фильтра может быть использован в качестве входа в другой. Это очень важно и очень действенно, потому что имея почти бесчисленное множество комбинаций эффектов фильтрации, вы можете создать почти бесчисленное количество графических эффектов.
Каждый примитив фильтра может принимать один или два входа и выводить только один результат. Входные данные примитива фильтра определяются в атрибуте in. Результат операции определяется в атрибуте result. Если эффекту фильтра требуется второй ввод, то он указывается в атрибуте in2. Результат операции может использоваться в качестве входных данных для любой другой операции, но если входные данные операции не указаны в атрибуте in, то результат предыдущей операции автоматически используется в качестве входных данных. Если не указать result примитива, то его результат будет автоматически использоваться в качестве входных данных для следующего примитива. Это станет яснее, когда мы начнем изучать примеры кода.
В качестве входных данных примитив фильтра может использовать и другие типы данных, наиболее важными из которых являются:
- SourceGraphic: элемент, к которому применяется весь фильтр; например, изображение или фрагмент текста.
- SourceAlpha: это то же самое, что и SourceGraphic, за исключением того, что эта графика содержит только альфа-канал элемента. Например, для изображения JPEG это черный прямоугольник размером с само изображение.
Вы обнаружите, что иногда вы хотите использовать источник графики в качестве входных данных, а иногда только ее альфа-канал. Примеры, которые мы рассмотрим в этой и следующих статьях обеспечат четкое понимание того, когда и что использовать.
Этот фрагмент кода является примером того, как может выглядеть фильтр с пакетом примитивов фильтра в качестве дочерних элементов. Не беспокойтесь о примитивах и о том, что они делают. На этом этапе просто обратите внимание на то, как определяются и используются входы и выходы определенных примитивов. Я добавила некоторые комментарии для помощи.
Теперь последняя концепция, которую я хочу кратко пояснить, прежде чем перейти к нашему первому примеру фильтра, это концепция области фильтрации (Filter Region).
Область фильтрации
Набору операций фильтрации требуется область для обработки, область, к которой они могут быть применены. Например, у вас может быть сложный SVG со многими элементами, и вы хотите применить эффект фильтрации только к определенной области или одному или группе элементов внутри того самого SVG.
В SVG элементы имеют «области», границы которых определяются гранями прямоугольника, ограничивающего элемент. Ограничивающий прямоугольник (также сокращенно «bbox») — это наименьший охватывающий прямоугольник вокруг элемента. Например, на следующем рисунке для фрагмента текста такой прямоугольник выделен розовым цветом.
Рис_3. Наименьший охватывающий прямоугольник вокруг фрагмента текста.
Обратите внимание, что этот прямоугольник может содержать еще несколько пробелов по вертикали, так как при вычислении высоты ограничивающего прямоугольника учитывается высота строки текста.
По умолчанию областью фильтра элемента является окаймляющей рамкой элемента. Поэтому, если вы примените эффект фильтра к нашему фрагменту текста, эффект будет ограничен этим прямоугольником, и любой результат фильтрации, который находится за его пределами, будет обрезан. Хотя это и разумно, это не очень практично, потому что многие фильтры будут воздействовать на некоторые пиксели за пределами ограничивающего прямоугольника, а по умолчанию эти пиксели в конечном итоге будут отрезаны.
Например, если применить эффект размытия к нашему фрагменту текста, можно увидеть что оно обрезается по левому и правому краям прямоугольника, ограничивающего текст:
Рис_4. Эффект размытия, применяемый к тексту, обрезается как с правой, так и с левой стороны области прямоугольника, ограничивающего текст.
Так как же нам предотвратить это? Ответ такой: путем расширения области фильтра. Мы можем расширить область, к которой применяется фильтр, изменив атрибуты x, y, width и height элемента < filter>.
Согласно спецификации,
Часто необходимо предусматривать поля в области фильтра, так как эффект фильтра может воздействовать на некоторые биты за пределами ограничивающего прямоугольника для данного объекта. Для этого можно давать отрицательные значения в процентах свойствам X и Y, а значения больше 100% свойствам width и height.
По умолчанию фильтры имеют области, расширяющие на 10% ширину и высоту ограничивающего прямоугольника во всех четырех направлениях. Другими словами, по умолчанию для атрибутов x, y, width и height используются следующие значения:
Если вы не включите эти атрибуты в элемент < filter>, то будут использоваться значения по умолчанию. Вы также можете переопределить их, чтобы расширить или уменьшить область по мере необходимости.
Следует иметь в виду, что единицы измерения, используемые в атрибутах x, y, width и height, зависят от того, какое значение атрибута filterUnits используется. Он определяет систему координат атрибутов x, y, width и height и может принимать одно из двух значений:
- objectBoundingBox. Это значение по умолчанию. Когда для filterUnits задано значение objectBoundingBox, то значения атрибутов x, y, width и height являются процентами или долями размера ограничивающего прямоугольника элемента. Это также означает, что вы можете использовать дробные значения вместо процентов, если вы предпочитаете.
- userSpaceOnUse Если для filterUnits задано значение userSpaceOnUse, координаты атрибутов x, y, width и height задаются относительно текущей системы координат, используемой пользователем. Другими словами, это относительно текущей системы координат, используемой в SVG, которая использует пиксели в качестве единиц измерения и, как правило, относительно размера самой SVG, предполагая, что значения viewBox соответствуют значениям исходной системы координат.
Все, что вам нужно знать о системах координат в SVG Вы можете узнать в этой статье, которую я написала несколько лет назад.
Быстрый совет: визуализация текущей области фильтра с помощью feFlood
Если вам когда-нибудь понадобится увидеть пределы области фильтра, вы можете визуализировать ее, залив каким-нибудь цветом. Удобно, что существует примитив фильтра под названием feFlood, единственной целью которого является именно это: заполнить текущую область фильтра цветом, указанным в атрибуте flood-color.
Итак, если предположить, что у нас есть фрагмент текста, область фильтра которого мы хотим увидеть, то код может выглядеть примерно так:
Как видно из приведенного выше фрагмента кода, примитив feFlood также принимает атрибут flood-opacity, который можно использовать для создания прозрачности цветного слоя заливки.
Приведенный выше фрагмент заливает область фильтра розовым цветом. Но вот в чем дело: когда вы заливаете область цветом, вы буквально заливаете ее цветом, что означает, что цвет будет охватывать все в области фильтра, включая любые элементы и эффекты, которые вы создали раньше, а также сам текст. В конце концов, таково понятие заливки, верно?
Рис_5. До и после заливки цветом текстовой области фильтра.
Чтобы это изменить, нам нужно переместить цветовой слой ниже, а сверху показать исходный текстовый слой.
Если у вас существует несколько слоев контекста, которые должны отображаться в фильтре SVG поверх друг друга, можно использовать примитив фильтра < feMerge>. Как следует из названия, примитив feMerge используется для объединения вместе слоев элементов или эффектов.
Примитив не имеет атрибута in. Для объединения слоев внутри < feMerge> используются два или более < feMergeNode>, каждый из которых имеет свой атрибут in, представляющий слой, который мы хотим добавить.
Укладка слоя (или «узла») зависит от порядка источника < feMergeNode> — первый < feMergeNode> отображается «за» или «ниже» второго. Последний < feMergeNode> представляет самый верхний слой. И так далее.
Итак, в нашем примере с текстом цветная заливка это слой, а текстовый источник (источник графики) — это другой слой, и мы хотим поместить текст поверх цветной заливки. Наш код будет выглядеть следующим образом:
Обратите внимание, как я назвала результат работы feFlood в атрибуте result, чтобы я могла использовать это имя в слое < feMergeNode> в качестве входных данных. Поскольку мы хотим отобразить исходный текст поверх цвета потока, мы ссылаемся на этот текст с помощью SourceGraphic. Следующее демо показывает результат:
Применение тени к изображению
Позвольте мне начать с краткого предупреждения: вам лучше создать простую тень с помощью CSS-функции фильтрации drop-shadow (). Путь фильтра SVG гораздо более подробный. Ведь, как мы уже упоминали ранее, функции CSS фильтра — это удобные ярлыки. Но я все равно хочу рассмотреть этот пример как простую точку входа в более сложные эффекты фильтра, которые мы рассмотрим в следующих статьях.
Итак, как создается тень?
Тень обычно представляет собой светло-серый слой позади или под элементом, который имеет ту же форму (или фигуру), что и сам элемент. Другими словами, вы можете считать это размытой серой копией элемента.
При создании SVG-фильтров нужно рассуждать поэтапно. Какие шаги необходимы для достижения того или иного эффекта? Для тени размытая серая копия элемента может быть создана путем размытия черной копии элемента, а затем раскрашивания этой черной копии, т.е. сделать ее серой. Затем эта только что созданная размытая серая копия помещается позади исходного элемента и немного смещается в обоих направлениях.
Итак, мы начнем с получения черной копии нашего элемента и ее размытия. Черная копия может быть создана с помощью альфа-канала элемента, используя SourceAlpha в качестве входных данных фильтра.
Примитив feGaussianBlur будет использоваться для применения размытия Гаусса к этому SourceAlpha-слою. Необходимое количество размытия задается в атрибуте stdDeviation (сокращение от Standard Deviation). Если задать одно значение атрибута stdDeviation, это значение будет использоваться для применения равномерного размытия входных данных. Можно также указать два числовых значения, тогда первое будет использоваться для размытия элемента в горизонтальном направлении, а второе — для вертикального размытия. Для тени нам нужно применить равномерное размытие, поэтому наш код будет начинаться с этого:
Приведенный выше фрагмент кода приводит к следующему эффекту, где на данный момент отображается только размытый альфа-канал изображения:
Рис_6. Созданная черная копия изображения с размытием (тень).
Затем мы хотим изменить цвет тени и сделать ее серой. Мы сделаем это, применив цветную заливку к области фильтра, а затем соединим этот слой цветной заливки с созданным нами слоем тени.
Совмещение-это соединение графического элемента с размытым фоном. Размытым фоном является контент позади элемента, с которым элемент совмещается. В нашем фильтре цвет заливки — это верхний слой, а размытая тень — ее фон, потому что она лежит за ним. Мы будем рассматривать примитив feComposite подробнее в следующих статьях, поэтому, если вы не знакомы с композицией и как она работает, то я рекомендую ознакомиться с подробной статьей на эту тему в моем блоге.
Примитив feComposite имеет атрибут operator, который используется для указания, какую составную операцию мы хотим использовать.
Используя оператор in composite, слой цветной заливки будет «обрезан», а отображаться будет только область цвета, которая совпадает с нашим теневым слоем. Оба слоя будут смешаны там, где они пересекаются, т.е. серый цвет будет использоваться для окрашивания нашей черной тени.
Примитив feComposite требует двух входных данных для работы, указанных в атрибутах in и in2. Первый вход — это наш цветной слой, а второй — размытый теневой фон. С помощью составной операции, указанной в атрибуте operator, наш код теперь выглядит следующим образом:
Обратите внимание, как результаты feGaussianBlur и примитивов feFlood используются в качестве входных данных для композита. Теперь наше демо выглядит так:
Рис_7. Теперь тень серая.
Перед тем, как мы наложим наше исходное изображение поверх тени, мы хотим сместить ее по вертикали и/или горизонтали. На сколько и в каком направлении полностью зависит от вас. Для этой демо я предположу, что у нас есть источник света, исходящего из верхнего левого угла нашего экрана, поэтому я смещу тень на несколько пикселей вниз и вправо.
Для смещения слоя в SVG используется примитив feOffset. Дополнительно к атрибутам in и result этот примитив принимает два главных атрибута: dx и dy, которые определяют расстояние, на которое вы хотите сместить слой вдоль осей X и Y соответственно.
После смещения тени, мы будет объединить ее с исходным изображением, используя feMerge, подобно тому, как мы объединяли текст и цветную заливку на предыдущем шаге — один mergeNode примет наши тени в качестве входных данных, а на другом mergeNode будет слой исходного изображения, используя SourceGraphic в качестве входных данных. Наш код теперь выглядит так:
И следующее демо для кода выше:
И именно так вы применяете эффект фильтра в SVG, используя фильтры SVG. Вы найдете, что этот эффект работает во всех основных браузерах.
Есть другой способ…
Существует другой, более распространенный способ создания тени. Вместо того, чтобы создавать черную тень и применять к ней цвет, чтобы сделать ее светлее, вы можете применить к ней прозрачность, сделав ее полупрозрачной и, следовательно, светлее.
В предыдущем демо мы узнали, как применить цвет к тени, используя feFlood, технику окраски, которую вы, вероятно, посчитаете нужной и станете часто пользоваться. Вот почему я подумала, что ее необходимо рассмотреть. Это также полезно узнать, потому что это путь, которым вы захотите создать тень, которая, по какой-то причине, будет пестрой, например, вместо черной или серой.
Для изменения прозрачности слоя можно использовать либо примитив feColorMatrix, либо feComponentTransfer. О примитиве feComponentTransfer я расскажу более подробно в следующих статьях, поэтому сейчас воспользуемся feColorMatrix, чтобы уменьшить непрозрачность нашей тени.
Примитив feColorMatrix заслуживает отдельной статьи. На данный момент я настоятельно рекомендую прочитать статью Una Kravet, которая будет отличным введением с действительно хорошими примерами.
Короче говоря, этот фильтр применяет матричное преобразование к каналам R (Красный), G (зеленый), B (синий) и A (Альфа) каждого пикселя на входной графике для получения результата с новым набором цветов и Альфа-значений. Другими словами, для управления цветами объекта используется матричная операция. Основа матрицы цвета выглядит так:
Еще раз рекомендую проверить статью Уны, чтобы узнать больше о ее синтаксисе.
Поскольку мы хотим только уменьшить непрозрачность нашей тени, мы будем использовать тождественную матрицу, которая не изменяет каналы RGB, но мы уменьшим значение альфа-канала в этой матрице:
А это наше живое демо:
Заключение
В этой серии я постараюсь отойти от очень технических определений операций с фильтрами и придерживаться упрощенных и понятных определений. Зачастую вам не нужно вдаваться в мелкие детали того, что происходит под капотом. Поэтому вхождение в эти мелочи только добавит сложности статьям, пожалуй сделает их менее понятными и принесет мало пользы. Понимание того, что делает фильтр и как его использовать, на мой взгляд, более чем достаточно, чтобы использовать преимущества, которые он может предложить. Если вы хотите получить более подробную информацию, я рекомендую для начала проконсультироваться со спецификацией. Тем не менее, она может оказаться мало полезной, поэтому вы, в конечном итоге проведете свое собственное исследование на стороне. Я приведу список отличных ресурсов для дальнейшего изучения в заключительной статье этой серии.
Теперь, когда мы рассмотрели основы SVG-фильтров и способы их создания и применения, мы рассмотрим дополнительные примеры эффектов использования дополнительных примитивов фильтров в следующих статьях. Оставайтесь с нами.