Создание реалистичного эффекта стекла в SVG
Сегодня я расскажу, как создать эффект стеклянного текста с помощью SVG.
Шаг 1: Размещаем основное изображение

Сначала нужно указать адрес основного изображение. Для этого используются тег <svg> с элементом <image> внутри него. Размеры атрибута viewBox и элемента <image> в SVG одинаковы. Это гарантирует, что элемента <image> будет одинакового размера с исходным изображением.
Сам файл SVG не содержит растровое изображение. Но мы можем разместить ссылку на него в SVG-коде.
Шаг 2: Обработка изображения
Обработаем изображение с помощью фильтра. Создаем еще один <svg> для хранения фильтров.
Первый фильтр (#displacement) искажает изображение. В нем используются два примитива: feTurbulence и feDisplacementMap. Они воссоздают на изображении эффект вибрации.
После добавления фильтра применим его к элементу <image>.
HTML
<svg>
<!—- еще код -->
<!—ИСКАЖЕННОЕ ИЗОБРАЖЕНИЕ: вырезанное -->
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
<!-- ФИЛЬТР: эффект дрожания -->
<filter id="distortion">
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<!-- еще код -->
</svg>
Шаг 3: Вырезаем текст
Для этого нужно добавить элемент <text> в тег <clip-path> и присвоить ему id. Это ограничивает область вырезанного текста контейнером <text>.
Шаг 4: Открываем изображение полностью
Но после того как мы вырезали текст, основная область изображения исчезла. Это можно исправить, добавив копию оригинального изображения.

Можно было бы добавить фильтр в <text> и использовать свойство in=“BackgroundImage в feDisplacementMap, чтобы исказить картинку за текстом. Но этот способ поддерживается не всеми браузерами, поэтому придется работать с несколькими изображениями.
HTML
<svg>
<!—- еще код -->
<!—ФОНОВОЕ ИЗОБРАЖЕНИЕ - видимое -->
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
<!-- ИСКАЖЕННОЕ ИЗОБРАЖЕНИЕ: вырезанное -->
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
<!—- еще код -->
</svg>
Шаг 5: Снова размещаем текст
Создадим копию слоя с текстом. Теперь у нас должно получить изображение с черным текстом сверху.
HTML
<svg>
<!— еще код -->
<!-- ТЕКСТ - вырезанный -->
<clipPath id="clip">
<text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
</clipPath>
<!-- TЕКСТ - видимый -->
<text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
<!—- еще код -->
</svg>
Шаг 6: Создаем темный край текста
Создадим темный край вокруг текста, который в сочетании со светлым краем добавит глубины. Для этого добавим новый фильтр и присвоим ему id=“textFilter". После чего сошлемся на него в атрибуте filter элемента <text>.
Теперь нужно воссоздать тень, которую будет отбрасывать стекло. Для реализации этого эффекта мы используем примитива: feMorphology, feOffset, feFlood и feComposite.
feMorphology сделает текст толще. В следующей версии примера закомментируйте три примитива (feOffset, feFlood, feComposite) и поэкспериментируйте с feMorphology.
feOffset используется для перемещения всех «пикселей» в предыдущем примитиве (feMorphology). Значения dx='5' и dy='5' перемещают «пиксели» по оси x и по оси y соответственно. Чем больше число, тем дальше они перемещаются. Установите для dx отрицательное значение и «пиксели» переместятся влево. При отрицательном значении dy они будут двигаться вверх.
Я пишу слово «пиксели» в кавычках, потому что это не пиксели экрана. Это размеры, установленные для родительского элемента <svg>. Я воспринимаю их как проценты. В примере, приведенном ранее, мы использовали настройки viewBox= “0 0 1890 1260”. Это значит, что ширина контейнера <svg> составляет 1890 «пикселей». Если мы установим dx=“189”, элемент переместится по холсту SVG на 10%.
feFlood – отличный примитив для заливки указанной области изображения! Но сейчас мы не сможем прочитать текст, так как фильтр просто заполняет определенную область цветом.
feComposite поможет исправить эту проблему. Этот примитив влияет на альфа-слой канала in в канале in2.
HTML
<svg>
<!—- еще код -->
<!—- темный край -->
<feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
<feConvolveMatrix order="3,3" kernelMatrix=
"1 0 0
0 1 0
0 0 1" in="dark_edge_01" result="dark_edge_02" />
<feOffset dx="5" dy="5" in="dark_edge_02" result="dark_edge_03"/>
<feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
<feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />
</filter>
<!—- еще код -->
</svg>
Шаг 7: Создаем светлый край
Теперь нам нужно сместить фигуру вверх и влево, используя отрицательные значения dx/dy. На этот раз установим белый цвет. Наша цель – достичь хорошего эффекта глубины. Но мы не будем использовать feComposite, потому что альфа-канал темного края не должен закрашиваться светлым краем.
HTML
<svg>
<filter id="textFilter">
<!—- еще код -->
<feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
<feConvolveMatrix order="3,3" kernelMatrix=
"1 0 0
0 1 0
0 0 1" in="light_edge_01" result="light_edge_02" />
<feOffset dx="-2" dy="-2" in="light_edge_02" result="light_edge_03"/>
<feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
<feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />
<!—- еще код -->
</filter>
</svg>
Шаг 8: Соединяем края
Примитив feMerge позволяет объединять любое количество примитивов, создавая совершенно новую картинку. С его помощью мы отобразим темный и светлый край одновременно. Поскольку мы использовали feMorphology, чтобы сделать буквы жирнее, теперь можно вырезать исходные формы букв из результата работы feMerge.
HTML
<svg>
<filter id="textFilter">
<!—- еще код -->
<feMerge result="edges">
<feMergeNode in="dark_edge" />
<feMergeNode in="light_edge" />
</feMerge>
<feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />
</filter>
</svg>
Шаг 9: Наклон
Получился хороший эффект 3D-зеркала. Но буквы все еще выглядят плоскими. Сделаем их более округлыми с помощью эффекта наклона. Сначала используем примитив feGaussianBlur. Он немного размоет уже существующие фильтры. Затем применим feSpecularLighting. Оригинально изображение темное, поэтому установим для атрибута lighting-color какой-нибудь яркий цвет. Если выбранное изображение слишком яркое, это может затруднить чтение букв. В этом случае можно задать для lighting-color более темный оттенок.
HTML
<svg>
<filter id="textFilter">
<!-- еще код -->
<feGaussianBlur stdDeviation="5" result="bevel_blur" />
<feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
<feDistantLight azimuth="25" elevation="40" />
</feSpecularLighting>
<feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />
</filter>
</svg>
Шаг 10: Соединяем все вместе.
Теперь применим последний примитив feMerge, чтобы увидеть окончательный результат.
HTML
<svg>
<filter id="textFilter">
<!-- еще код -->
<feMerge result="complete">
<feMergeNode in="edges_complete" />
<feMergeNode in="bevel_complete" />
</feMerge>
</filter>
</svg>
HTML
<!-- ВИДИМЫЙ SVG -->
<svg viewBox="0 0 1890 1260">
<!-- ФОНОВОЕ ИЗОБРАЖЕНИЕ - видимое -->
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
<!-- ИСКАЖЕННОЕ ИЗОБРАЖЕНИЕ: вырезанное -->
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
<!-- ТЕКСТ - вырезанный -->
<clipPath id="clip">
<text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
</clipPath>
<!-- ТЕКСТ - видимый -->
<text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle" filter="url(#textFilter)">KYOTO</text>
</svg>
<!-- ФИЛЬТРЫ -->
<svg>
<filter id="distortion">
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<filter id="textFilter">
<!-- темный край -->
<feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
<feOffset dx="5" dy="5" in="dark_edge_01" result="dark_edge_03"/>
<feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
<feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />
<!-- светлый край -->
<feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
<feOffset dx="-2" dy="-2" in="light_edge_01" result="light_edge_03"/>
<feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
<feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />
<!-- края вместе -->
<feMerge result="edges">
<feMergeNode in="dark_edge" />
<feMergeNode in="light_edge" />
</feMerge>
<feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />
<!-- наклон -->
<feGaussianBlur stdDeviation="5" result="bevel_blur" />
<feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
<feDistantLight azimuth="25" elevation="40" />
</feSpecularLighting>
<feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />
<!-- все на своем месте -->
<feMerge result="complete">
<feMergeNode in="edges_complete" />
<feMergeNode in="bevel_complete" />
</feMerge>
</filter>
</svg>