SVG Фильтры (Часть 1)

Это первая статья в серии о фильтрах SVG. Данное руководство поможет вам понять, что они из себя представляют, и покажет, как их использовать для создания собственных визуальных эффектов.
В настоящее время CSS предоставляет нам способ применять цветовые эффекты к изображениям, такие как насыщенность, яркость и контрастность, среди прочих эффектов, через свойство filter и функции фильтра, которые поставляются с ним.
Теперь у нас есть 11 функций фильтра в CSS, которые выполняют целый ряд эффектов: от размытия до изменения цветового контраста, насыщенности и т. д.
Хотя мощные и очень удобные CSS-фильтры также очень ограничены. Эффекты, которые мы можем создать с их помощью, часто применимы к изображениям и ограничиваются манипулированием цветом и базовым размытием. Поэтому для создания более мощных эффектов, которые мы можем применить к более широкому диапазону элементов, нам потребуется более широкий набор функций. Эти функции доступны сегодня - и доступны уже более десяти лет - в SVG. В этой статье, которая является первой в серии о фильтрах SVG, вы узнаете о функциях фильтра SVG, и о том, как их использовать.
CSS-фильтры импортированы из SVG. Это довольно оптимизированные версии поднабора эффектов фильтров, присутствующих в SVG, которые использовались в спецификации SVG годами.
В SVG больше эффектов фильтров, чем в CSS, а версии SVG более мощные и способны к гораздо более сложным эффектам, чем их сочетания клавиш CSS. Например, в настоящее время можно размыть элемент, используя функцию фильтра CSS blur (). Применение эффекта размытия с помощью этой функции создаст равномерное размытие по Гауссу для элемента, к которому он применяется. На следующем изображении показан результат применения размытия 6px к изображению в CSS:
Функция blur () создает эффект размытия, который равномерно применяется к изображению в обоих направлениях - X и Y. Но эта функция - просто упрощенный и ограниченный ярлык для примитива фильтра размытия, доступного в SVG, который позволяет нам размывать изображение либо равномерно, либо применять эффект однонаправленного размытия вдоль оси X или Y.
Результат применения размытия по осям x и y соответственно с использованием SVG-фильтров.
Фильтры SVG могут применяться к элементам HTML, а также к элементам SVG. Эффект фильтра SVG можно применить к элементу HTML в CSS с помощью функции фильтра url(). Например, если у вас есть эффект фильтра с ID "myAwesomeEffect", определенным в вашей SVG (вскоре мы поговорим об определении эффектов фильтров в SVG), вы можете применить этот эффект к элементу HTML или изображению, например так:
Код
.el {
filter: url(#myAwesomeEffect);
}
filter: url(#myAwesomeEffect);
}
Лучше всего то, что вы увидите в этой серии: фильтры SVG способны создавать в браузере эффекты уровня Photoshop, используя несколько строк кода. Я надеюсь, что эта серия поможет раскрыть часть потенциала SVG фильтров и вдохновит вас начать использовать их в своих собственных проектах.
Но как насчет поддержки браузера, спросите вы?
Поддержка браузера
Поддержка браузеров для большинства фильтров SVG впечатляюще хороша. Однако применение эффекта может отличаться в разных браузерах в зависимости от поддержки браузером отдельных примитивов фильтра, используемых в эффекте фильтра SVG, а также в зависимости от возможных ошибок браузера. Поддержка браузера также может отличаться, когда фильтр SVG применяется к элементам SVG по сравнению с элементами HTML.
Хотя поддержка SVG Filter в целом хорошая, имейте в виду, что некоторые эффекты, которые мы рассмотрим позже в этой серии, можно считать экспериментальными. Я упомяну о любых серьезных проблемах или ошибках, если и когда они будут.
Итак, как вы определяете и создаете эффект фильтра в SVG?
Элемент
Точно так же, как линейные градиенты, маски, шаблоны и другие графические эффекты в SVG, фильтры имеют удобно названный выделенный элемент: элемент
Элемент
Причина этого в том, что для работы фильтра требуется исходное изображение, и если вы явно не определите это исходное изображение, вызвав фильтр для этого исходного изображения, у него не будет ничего для рендеринга, и поэтому он этого не делает.
Очень простой, минимальный пример кода, определяющий фильтр SVG и применяющий его к исходному изображению в SVG, будет выглядеть следующим образом:
Код
<svg width="600" height="450" viewBox="0 0 600 450">
<filter id="myFilter">
<!-- эффекты фильтра идут сюда -->
</filter>
<image xlink:href="..."
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>
<filter id="myFilter">
<!-- эффекты фильтра идут сюда -->
</filter>
<image xlink:href="..."
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>
Фильтр в приведенном выше примере кода ничего не делает на данный момент, потому что он пуст. Чтобы создать эффект фильтра, необходимо определить последовательность из одной или нескольких операций фильтра, которые создают этот эффект внутри фильтра. Другими словами, элемент фильтра является контейнером для последовательности операций фильтра, которые вместе создают эффект фильтра. Эти операции фильтрации называют «Примитивы фильтра» в SVG.
Примитивы фильтра
Итак, в SVG каждый элемент
Примитив фильтра удобно называть по имени любой графической операции, которую он выполняет. Например, примитив, который применяет эффект размытия по Гауссу к исходной графике, называется feGaussianBlur. Все примитивы имеют одинаковый префикс: fe, что сокращенно «фильтр эффекта». (Опять же, имена в SVG удобно выбирать, чтобы они напоминали, чем является элемент.)
В следующем фрагменте показано, как будет выглядеть простой фильтр, если бы этот фильтр применял к изображению размытие по Гауссу с разрешением 5px:
Код
<svg width="600" height="450" viewBox="0 0 600 450"></feGaussianBlur>
<filter id="myFilter">
<feGaussianBlur stDeviation="5"></feGaussianBlur>
</filter>
<image xlink:href="..."
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>
<filter id="myFilter">
<feGaussianBlur stDeviation="5"></feGaussianBlur>
</filter>
<image xlink:href="..."
width="100%" height="100%" x="0" y="0"
filter="url(#myFilter)"></image>
</svg>
В настоящее время в спецификации фильтра SVG определены 17 фильтрующих примитивов, которые способны создавать чрезвычайно мощные графические эффекты, включая генерацию шума и текстур, световые эффекты, управление цветом (для каждого канала в отдельности) и многое другое.
Примитив фильтра работает, принимая исходную графику в качестве входных данных и выводя другую. И выход одного эффекта фильтра может быть использован как вход для другого. Это очень важно и очень мощно, потому что это означает, что у вас есть почти бесчисленное множество комбинаций эффектов фильтра и, следовательно, вы можете создать почти бесчисленное количество графических эффектов.
Каждый примитив фильтра может принимать один или два входа и выводить только один результат. Ввод примитива фильтра определяется в атрибуте, который вызывается. Результат операции определяется в атрибуте результата. Если эффект фильтра занимает второй вход, второй вход устанавливается в атрибуте in2. Результат операции можно использовать в качестве входных данных для любой другой операции, но если входные данные операции не указаны в атрибуте in, результат предыдущей операции автоматически используется в качестве входных данных. Если вы не укажете результат примитива, его результат будет автоматически использоваться в качестве входных данных для следующего примитива. (Это станет яснее, когда мы начнем изучать примеры кода.)
Помимо использования результата (ов) других примитивов в качестве входных данных, примитив фильтра также принимает входные данные других типов, наиболее важными из которых являются:
- SourceGraphic: элемент, к которому применяется весь фильтр; например, изображение или фрагмент текста.
- SourceAlpha: это то же самое, что SourceGraphic, за исключением того, что этот рисунок содержит только альфа-канал элемента. Например, для изображения JPEG это черный прямоугольник размером с само изображение.
Вы обнаружите, что иногда вам захочется использовать исходную графику в качестве входных данных, а иногда только ее альфа-канал. Примеры, которые мы рассмотрим в этом посте и в следующих постах, дадут четкое понимание того, когда и какой использовать.
Этот фрагмент кода является примером того, как может выглядеть фильтр с кучей фильтрующих примитивов, как дети. Не беспокойтесь о примитивах и о том, что они делают. На этом этапе просто обратите внимание на то, как входы и выходы определенных примитивов определяются и используются среди них. Я добавил несколько комментариев, чтобы помочь.
Код
<svg width="600" height="400" viewBox="0 0 850 650">
<filter id=“filter">
<feOffset in="SourceAlpha" dx="20" dy=“20"></feOffset>
<!-- поскольку предыдущий фильтр не имел определенного результата, а следующий не имеет входного набора, результат указанного выше примитива автоматически используется в качестве входного для следующего фильтра -->
<feGaussianBlur stdDeviation="10" result=“DROP"></feGaussianBlur>
<!-- установка/определение имен результатов во всех заглавных буквах - хороший способ сделать их более различимыми, а весь код - более читабельным. -->
<feFlood flood-color="#000" result="COLOR"></feFlood>
<!-- Этот примитив использует выходные данные двух предыдущих примитивов в качестве входных данных и выводит новый эффект -->
<feComposite in="DROP" in2="COLOR" operator="in" result="SHADOW1"></feComposite>
<feComponentTransfer in="SHADOW1" result="SHADOW">
<feFuncA type="table" tableValues="0 0.5"></feFuncA>
</feComponentTransfer>
<!-- Вы можете использовать ЛЮБЫЕ два результата в качестве входных данных для любого примитива, независимо от их порядка в DOM. Следующий примитив является хорошим примером использования двух ранее сгенерированных выходных данных в качестве входных данных. -->
<feMerge>
<feMergeNode in="SHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#filter)"></image>
</svg>
<filter id=“filter">
<feOffset in="SourceAlpha" dx="20" dy=“20"></feOffset>
<!-- поскольку предыдущий фильтр не имел определенного результата, а следующий не имеет входного набора, результат указанного выше примитива автоматически используется в качестве входного для следующего фильтра -->
<feGaussianBlur stdDeviation="10" result=“DROP"></feGaussianBlur>
<!-- установка/определение имен результатов во всех заглавных буквах - хороший способ сделать их более различимыми, а весь код - более читабельным. -->
<feFlood flood-color="#000" result="COLOR"></feFlood>
<!-- Этот примитив использует выходные данные двух предыдущих примитивов в качестве входных данных и выводит новый эффект -->
<feComposite in="DROP" in2="COLOR" operator="in" result="SHADOW1"></feComposite>
<feComponentTransfer in="SHADOW1" result="SHADOW">
<feFuncA type="table" tableValues="0 0.5"></feFuncA>
</feComponentTransfer>
<!-- Вы можете использовать ЛЮБЫЕ два результата в качестве входных данных для любого примитива, независимо от их порядка в DOM. Следующий примитив является хорошим примером использования двух ранее сгенерированных выходных данных в качестве входных данных. -->
<feMerge>
<feMergeNode in="SHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#filter)"></image>
</svg>
Теперь последняя концепция, которую я хочу кратко осветить перед переходом к нашему первому примеру фильтра, - это концепция области фильтра.
Регион фильтра
Множеству операций фильтрации нужен регион для работы - область, к которой они могут быть применены. Например, у вас может быть сложный SVG со многими элементами, и вы хотите применить эффект фильтра только к определенной области или к одному или группе элементов внутри этого SVG.
В SVG элементы имеют «области», границы которых определяются границами ограничивающего прямоугольника элемента. Ограничительная рамка (также сокращенно «bbox») - это наименьший подходящий прямоугольник вокруг элемента. Так, например, для фрагмента текста наименьший подходящий прямоугольник выглядит как розовый прямоугольник на следующем изображении.
Наименьший подходящий прямоугольник вокруг фрагмента текста.
Обратите внимание, что этот прямоугольник может включать в себя больше пустого пространства по вертикали, потому что высота строки текста учитывается при расчете высоты ограничивающего прямоугольника.
Область фильтра по умолчанию для элемента - это ограничивающая рамка элемента. Таким образом, если вы примените эффект фильтра к нашему фрагменту текста, эффект будет ограничен этим прямоугольником, и любой результат фильтра, который находится за его пределами, будет обрезан. Хотя это и разумно, это не очень практично, поскольку многие фильтры будут слегка воздействовать на пиксели за пределами ограничивающей рамки, и по умолчанию эти пиксели будут обрезаны.
Например, если мы применим эффект размытия к нашему фрагменту текста, вы можете увидеть, как размытие обрезается по левому и правому краям ограничительного поля текста:
Эффект размытия, применяемый к тексту, обрезается как с правой, так и с левой стороны области ограничивающего прямоугольника текста.
Итак, как мы можем предотвратить это? Ответ: путем расширения области фильтра. Мы можем расширить область, к которой применяется фильтр, изменив атрибуты x, y, width и height элемента
Согласно спецификации,
Цитата
Часто необходимо обеспечить пространство для заполнения в области фильтра, потому что эффект фильтра может слегка воздействовать на биты за пределами плотно прилегающей ограничительной рамки на данном объекте. Для этих целей можно указать отрицательные процентные значения для "x" и "y", а процентные значения больше 100% для "width" и "height".
По умолчанию фильтры имеют области, расширяющие на 10% ширину и высоту ограничительной рамки во всех четырех направлениях. Другими словами, значения по умолчанию для атрибутов x, y, width и height следующие:
Код
<filter x="-10%" y="-10%" width="120%" height="120%"
filterUnits="objectBoundingBox">
<!-- операции фильтра здесь -->
</filter>
filterUnits="objectBoundingBox">
<!-- операции фильтра здесь -->
</filter>
Если вы опустите эти атрибуты в элементе
Следует иметь в виду, что единицы измерения, используемые в атрибутах x, y, width и height, зависят от того, какое значение filterUnits используется. Атрибут filterUnits определяет систему координат для атрибутов x, y, width и height. Требуется одно из двух значений:
- objectBoundingBox: это значение по умолчанию. Если для filterUnits задано значение objectBoundingBox, значения атрибутов x, y, width и height представляют собой проценты или доли от размера ограничивающего прямоугольника элемента. Это также означает, что вы можете использовать дроби в качестве значений вместо процентов, если вы предпочитаете.
- userSpaceOnUse: когда для filterUnits задано значение userSpaceOnUse, координаты атрибутов x, y, width и height устанавливаются относительно текущей используемой пользовательской системы координат. Другими словами, это относительно текущей системы координат, используемой в SVG, которая использует пиксели в качестве единицы и, как правило, относительно размера самого SVG, предполагая, что значения viewBox совпадают со значениями исходной системы координат.
Код
<!-- Использование объектов objectBoundingBox -->
<filter id="filter"
x=“5%" y=“5%” width="100%" height=“100%”>
<!-- Использование userSpaceOnUse -->
<filter id=“filter"
filterUnits="userSpaceOnUse"
x=“5px" y=“5px” width="500px" height="350px">
<filter id="filter"
x=“5%" y=“5%” width="100%" height=“100%”>
<!-- Использование userSpaceOnUse -->
<filter id=“filter"
filterUnits="userSpaceOnUse"
x=“5px" y=“5px” width="500px" height="350px">
Совет: Визуализация текущей области фильтра с помощью feFlood
Если вам когда-либо понадобится увидеть размер области фильтра, вы можете визуализировать ее, залив область фильтра цветом. Удобно, что существует примитив фильтра с именем feFlood, единственная цель которого состоит именно в том, чтобы: залить текущую область фильтра цветом, который вы указали в атрибуте flood-color.
Итак, если у нас есть фрагмент текста, область фильтра которого мы хотим визуализировать, код будет выглядеть так просто:
Код
<svg width="600px" height="400px" viewBox="0 0 600 400">
<filter id="flooder" x="0" y="0" width="100%" height="100%">
<feFlood flood-color="#EB0066" flood-opacity=".9"></feFlood>
</filter>
<text dx="100" dy="200" font-size="150" font-weight="bold" filter="url(#flooder)">Effect!</text>
</svg>
<filter id="flooder" x="0" y="0" width="100%" height="100%">
<feFlood flood-color="#EB0066" flood-opacity=".9"></feFlood>
</filter>
<text dx="100" dy="200" font-size="150" font-weight="bold" filter="url(#flooder)">Effect!</text>
</svg>
Как вы можете видеть в приведенном выше фрагменте кода, примитив feFlood также принимает атрибут непрозрачности, который вы можете использовать, чтобы сделать слой цветопередачи прозрачным.
Вышеуказанный фрагмент заливает область фильтра розовым цветом. Но вот в чем дело: когда вы заливаете регион цветом, вы буквально заливаете его цветом, что означает, что цвет будет охватывать все в области фильтра, включая любые элементы и эффекты, которые вы создали ранее, а также сам текст.
До и после заливки области фильтра текста цветом.
Чтобы это изменить, нам нужно переместить цветной слой на «зад» и показать верхний слой исходного текста.
Когда у вас есть несколько слоев контента, которые вы хотите отобразить друг над другом в SVG-фильтре, вы можете использовать примитив фильтра
Примитив
Укладка слоя (или «узла») зависит от порядка источника
Итак, в нашем примере с текстом цвет заливки - это слой, а исходный текст (исходный рисунок) - это другой слой, и мы хотим поместить текст поверх цвета заливки. Наш код будет выглядеть следующим образом:
Код
<svg width="600px" height="400px" viewBox="0 0 600 400">
<filter id="flooder">
<feFlood flood-color="#EB0066" flood-opacity=".9" result="FLOOD"></feFlood>
<feMerge>
<feMergeNode in="FLOOD" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<text dx="100" dy="200" font-size="150" font-weight="bold" filter="url(#flooder)">Effect!</text>
</svg>
<filter id="flooder">
<feFlood flood-color="#EB0066" flood-opacity=".9" result="FLOOD"></feFlood>
<feMerge>
<feMergeNode in="FLOOD" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<text dx="100" dy="200" font-size="150" font-weight="bold" filter="url(#flooder)">Effect!</text>
</svg>
Обратите внимание, как был назван результат feFlood в атрибуте result, чтобы я мог ссылаться на это имя в
See the Pen Filter Region Visualization with feFlood by Sara Soueidan (@SaraSoueidan) on CodePen.light
Теперь, когда мы познакомились с миром фильтров SVG с помощью этой демонстрации, давайте создадим простую тень SVG.
Применение тени к изображению
Вам лучше создать простую тень с помощью функции фильтра CSS drop-shadow(). Фильтр SVG гораздо более многословен. В конце концов, как мы упоминали ранее, функции фильтра CSS являются удобными ярлыками. Но я все равно хочу рассмотреть этот пример как простую точку входа в более сложные эффекты фильтров, о которых мы расскажем в следующих статьях.
Итак, как получается тень?
Тень капли обычно представляет собой светло-серый слой позади или под элементом, который имеет ту же форму (или форму), что и сам элемент. Другими словами, вы можете думать об этом как о размытой серой копии элемента.
При создании фильтров SVG, мы должны думать поэтапно. Какие шаги необходимы для достижения определенного эффекта? Для тени можно создать размытую серую копию элемента, размывая черную копию элемента и затем окрашивая эту черную копию (делая ее серой). Затем эта вновь созданная размытая серая копия располагается позади исходного элемента и немного смещается в обоих направлениях.
Итак, мы собираемся начать с получения черной копии нашего элемента и размытия ее. Черную копию можно создать, используя альфа-канал элемента, используя SourceAlpha в качестве входных данных для нашего фильтра.
Примитив feGaussianBlur будет использоваться для применения размытия по Гауссу к этому слою SourceAlpha. Степень размытия указывается в атрибуте stdDeviation (сокращение от: Standard Deviation). Если вы предоставите одно значение атрибуту stdDeviation, это значение будет использоваться для применения равномерного размытия к входным данным. Вы также можете указать два числовых значения: первое будет использоваться для размытия элемента в горизонтальном направлении, а второе - для применения вертикального размытия. Для тени нам нужно применить равномерное размытие, поэтому наш код начнется с этого:
Код
<svg width="600" height="400" viewBox="0 0 850 650">
<filter id="drop-shadow">
<-- Grab a blakc copy of the source image and blur it by 10 -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
<filter id="drop-shadow">
<-- Grab a blakc copy of the source image and blur it by 10 -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
Приведенный выше фрагмент кода приводит к следующему эффекту, когда в этой точке отображается только размытый альфа-канал изображения:
Далее мы хотим изменить цвет тени и сделать ее серой. Мы сделаем это, применив цвет заливки к области фильтра, а затем скомбинируем этот цветовой слой с созданным нами слоем тени.
В нашем фильтре цвет Flood - это верхний слой, а размытая тень - его фон (потому что он лежит за ним). Мы еще увидим примитив feComposite в следующих статьях.
Примитив feComposite имеет атрибут оператора, который используется для указания, какую составную операцию мы хотим использовать.
При использовании встроенного оператора цветовой слой будет «обрезан», и будет визуализирована только та область цвета, которая перекрывается с нашим теневым слоем, и два слоя будут смешаны там, где они пересекаются, что означает, что серый цвет будет использоваться, чтобы раскрасить нашу черную тень.
Примитиву feComposite требуется два входа для работы, указанные в атрибутах in и in2. Первый вход - это наш цветовой слой, а второй - наш размытый теневой фон. С составной операцией, указанной в атрибуте operator, наш код теперь выглядит так:
Код
<svg width="600" height="400" viewBox="0 0 850 650">
<filter id="drop-shadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<feFlood flood-color="#bbb" result="COLOR"></feFlood>
<feComposite in="COLOR" in2="DROP" operator="in" result="SHADOW"></feComposite>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
<filter id="drop-shadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<feFlood flood-color="#bbb" result="COLOR"></feFlood>
<feComposite in="COLOR" in2="DROP" operator="in" result="SHADOW"></feComposite>
</filter>
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
Обратите внимание, как результаты примитивов feGaussianBlur и feFlood используются в качестве входных данных для feComposite. Наше демо теперь выглядит так:
Перед тем, как нанести исходное изображение поверх тени, мы хотим сместить ее по вертикали и / или по горизонтали. Как сильно вы смещаете тень и в каком направлении полностью зависит от вас. Для этой демонстрации я предполагаю, что у нас есть источник света, исходящий из верхнего левого угла нашего экрана, поэтому я переместу его на несколько пикселей вниз вправо.
Чтобы сместить слой в SVG, мы используем примитив feOffset. В дополнение к атрибутам in и result этот примитив принимает два основных атрибута: dx и dy, которые определяют расстояние, на которое вы хотите сместить слой вдоль осей x и y соответственно.
После смещения тени, мы слим ее с исходным изображением, используя feMerge, аналогично тому, как мы слили текст и цвет заливки в предыдущем разделе - один mergeNode примет нашу тень в качестве ввода, а другой mergeNode наложит слой на исходное изображение. используя SourceGraphic в качестве входных данных. Наш финальный код теперь выглядит так:
Код
<svg width="600" height="400" viewBox="0 0 850 650">
<filter id="drop-shadow">
<!-- Получить исходную альфу и размыть ее; назовем результат "DROP" -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<!-- зальем область светло-серым цветом; мы назовем этот слой "COLOR" -->
<feFlood flood-color="#bbb" result="COLOR"></feFlood>
<!-- Скомпонуем слои DROP и COLOR, чтобы раскрасить тень. Результат называется "SHADOW" -->
<feComposite in="COLOR" in2="DROP" operator="in" result="SHADOW"></feComposite>
<!-- Переместим слой SHADOW на 20 пикселей вниз и вправо. Новый слой теперь называется "DROPSHADOW" -->
<feOffset in="SHADOW" dx="20" dy="20" result="DROPSHADOW"></feOffset>
<!-- Нанесем слой DROPSHADOW и исходное изображение, убедившись, что изображение расположено сверху (помните: порядок MergeNode имеет значение) -->
<feMerge>
<feMergeNode in="DROPSHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<!-- Применим фильтр к исходному изображению в атрибуте `filter` -->
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
<filter id="drop-shadow">
<!-- Получить исходную альфу и размыть ее; назовем результат "DROP" -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<!-- зальем область светло-серым цветом; мы назовем этот слой "COLOR" -->
<feFlood flood-color="#bbb" result="COLOR"></feFlood>
<!-- Скомпонуем слои DROP и COLOR, чтобы раскрасить тень. Результат называется "SHADOW" -->
<feComposite in="COLOR" in2="DROP" operator="in" result="SHADOW"></feComposite>
<!-- Переместим слой SHADOW на 20 пикселей вниз и вправо. Новый слой теперь называется "DROPSHADOW" -->
<feOffset in="SHADOW" dx="20" dy="20" result="DROPSHADOW"></feOffset>
<!-- Нанесем слой DROPSHADOW и исходное изображение, убедившись, что изображение расположено сверху (помните: порядок MergeNode имеет значение) -->
<feMerge>
<feMergeNode in="DROPSHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<!-- Применим фильтр к исходному изображению в атрибуте `filter` -->
<image xlink:href="..." x="0" y="0" width="100%" height="100%" filter="url(#drop-shadow)"></image>
</svg>
И ниже приведена живая демонстрация приведенного выше кода:
See the Pen Drop Shadow: Tinted shadow with feComposite by Sara Soueidan (@SaraSoueidan) on CodePen.light
И именно так вы применяете эффект фильтра в SVG, используя фильтры SVG. Вы обнаружите, что этот эффект работает во всех основных браузерах.
Есть другой способ ...
Существует еще один, более распространенный способ создания тени. Вместо создания черной тени и применения к ней цвета, чтобы сделать его светлее, вы можете применить к нему прозрачность, что сделает его полупрозрачным и, следовательно, светлее.
В предыдущей демонстрации мы узнали, как применить цвет к тени, используя feFlood, метод раскраски, который вам, вероятно, понадобится и который вы будете часто использовать. Вот почему я подумал, что это необходимо для покрытия. Также полезно учиться, потому что это путь, если вы хотите создать тень, которая по какой-либо причине имеет красочную тень, например, вместо черной или серой.
Чтобы изменить непрозрачность слоя, вы можете использовать либо примитив feColorMatrix, либо примитив feComponentTransfer. Я расскажу о примитиве feComponentTransfer более подробно в следующих статьях, поэтому сейчас я буду использовать feColorMatrix, чтобы уменьшить непрозрачность для нашей тени.
Примитив feColorMatrix заслуживает отдельной статьи.
Короче говоря, этот фильтр применяет матричное преобразование к каналам R (красный), G (зеленый), B (синий) и A (альфа) каждого пикселя входной графики, чтобы получить результат с новым набором цветов и альфа-значения. Другими словами, вы используете матричную операцию для манипулирования цветами вашего объекта. Основная цветовая матрица выглядит следующим образом:
Код
<filter id="myFilter">
<feColorMatrix
type="matrix"
values="R 0 0 0 0
0 G 0 0 0
0 0 B 0 0
0 0 0 A 0 "/>
</feColorMatrix>
</filter>
<feColorMatrix
type="matrix"
values="R 0 0 0 0
0 G 0 0 0
0 0 B 0 0
0 0 0 A 0 "/>
</feColorMatrix>
</filter>
Поскольку мы хотим только уменьшить непрозрачность нашей тени, мы будем использовать единичную матрицу, которая не изменяет каналы RGB, но мы уменьшим значение альфа-канала в этой матрице:
Код
<filter id="filter">
<!-- Получить исходную альфу и размыть ее -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<!-- сместить тень -->
<feOffset in="SHADOW" dx="20" dy="20" result="DROPSHADOW"></feOffset>
<!-- сделать тень прозрачной, уменьшив значение альфа-канала до 0.3 -->
<feColorMatrix type="matrix" in="DROPSHADOW" result="FINALSHADOW"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.3 0">
</feColorMatrix>
<!-- Объединить тень и исходное изображение -->
<feMerge>
<feMergeNode in="FINALHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<!-- Получить исходную альфу и размыть ее -->
<feGaussianBlur in="SourceAlpha" stdDeviation="10" result="DROP"></feGaussianBlur>
<!-- сместить тень -->
<feOffset in="SHADOW" dx="20" dy="20" result="DROPSHADOW"></feOffset>
<!-- сделать тень прозрачной, уменьшив значение альфа-канала до 0.3 -->
<feColorMatrix type="matrix" in="DROPSHADOW" result="FINALSHADOW"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.3 0">
</feColorMatrix>
<!-- Объединить тень и исходное изображение -->
<feMerge>
<feMergeNode in="FINALHADOW"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
Живая демонстрация:
See the Pen Drop Shadow: Translucent shadow with feColorMatrix by Sara Soueidan (@SaraSoueidan) on CodePen.light
В завершении
В этой серии статей я постарался держаться подальше от технических определений операций фильтра и придерживаться упрощенных и понятных определений. Зачастую вам не нужно вдаваться в мельчайшие подробности того, что происходит под капотом, так что проникновение в эти детали только увеличит сложность статей, возможно, сделает их менее усвояемыми и принесет мало пользы. На мой взгляд, понимания того, что делает фильтр и как его использовать, более чем достаточно, чтобы воспользоваться тем, что он может предложить. Если вы хотите получить более подробную информацию, я рекомендую обратиться к спецификации для начала. Тем не менее, спецификации могут оказаться мало полезными, так что вы, вероятно, в конечном итоге проведете собственное исследование на стороне. Я приведу список отличных ресурсов для дальнейшего изучения в заключительной статье этой серии.
Теперь, когда мы рассмотрели основы фильтров SVG и способы их создания и применения, мы рассмотрим больше примеров эффектов с использованием большего количества примитивов фильтров в следующих статьях. Оставайтесь в курсе.
Поделись с друзьями:
Добавлять комментарии могут только зарегистрированные пользователи.