css 实现长虹玻璃(Fluted)效果

11,480 阅读6分钟

起因

在网上冲浪时看到了一个非常炫酷得效果,自我学习掌握后又开发出来得长虹玻璃效果,第一时间分享给大家!

01.gif

如果对SVG滤镜感兴趣的朋友可以也去看看我其他的相关内容

什么是长虹玻璃效果(Fluted)

“长虹玻璃”效果是一种特殊的玻璃效果,通常具有条纹状的透明或半透明图案,类似于磨砂玻璃,但带有垂直或水平的纹理凹槽。这种玻璃在保持隐私的同时,还允许一定量的光线通过,常用于室内装饰、淋浴房、门窗等场合。

简单得说就是类似透过磨砂玻璃看物体时得效果,具体表现为垂直或水平得纹理凹槽,透过纹理得图像会产生一定程度得偏移!

本篇得核心功能是借助于svg滤镜效果来实现,如果还没有看过相关得知识可以猛击Coco大佬的一篇文章你所不知道的 CSS 滤镜技巧与细节 ,或者留言讨论!

如何实现?

现在我们大概了解了什么是所谓得长虹玻璃效果,核心是如何创造一个垂直纹理,透过纹理得内容会产生一定程度得偏移。

feDisplacementMap

<feDisplacementMap>是一个 SVG 滤镜原语,用于在图像上创建形变效果。它通过使用一个位移贴图来移动输入图像中的像素,从而产生扭曲、波纹或其他形变效果。以下是对 <feDisplacementMap> 的详细介绍:

属性

  • in: 定义输入图像。可以是另一个滤镜原语的结果,也可以是 "SourceGraphic"(原始图像)。
  • in2: 定义位移贴图图像。位移贴图中的颜色值决定了如何移动输入图像中的像素。
  • scale: 定义位移的强度。较高的值会产生更大的位移效果。
  • xChannelSelector: 定义位移贴图的哪个颜色通道用于水平位移。可选值为 "R"(红色)、"G"(绿色)、"B"(蓝色)和 "A"(透明度)。
  • yChannelSelector: 定义位移贴图的哪个颜色通道用于垂直位移。可选值与 xChannelSelector 相同。

如果只看定义得话会发现太抽象了,接下来我们举个实际得栗子!

<svg version="1.1" width="128" height="266" xmlns="http://www.w3.org/2000/svg" color-interpolation-filters="sRGB">

    <filter

            id="filter"
            width="128"
            height="128"
            x="0" y="0" filterUnits="userSpaceOnUse"
            color-interpolation-filters="sRGB">
        <feImage
                width="128"
                height="128"
                href="../img/09.jpg"/>
        <feDisplacementMap
                in="SourceGraphic"
                xChannelSelector="R"
                yChannelSelector="G"
                scale="50"/>
    </filter>

    <image height="128" width="128" href="../img/04.webp" filter="url(#filter)"/>

</svg>

02.png

可以发现我们两个图片融合后原图产生了明显得形变,具体形变得公式如下:

P'(x,y) ← P( x + scale * (XC(x,y) - .5), y + scale * (YC(x,y) - .5))

看着非常唬人但其实非常容易理解:

  • P′(x,y) 表示结果图像中一个像素的坐标;
  • x 和 y 是未过滤源图像中该像素的坐标;
  • XC 和 YC 是映射图中给定像素的标准化(1/255)RGB颜色值;

代入公式我们再来一个简单得栗子

    <svg version="1.1" width="128" height="128" xmlns="http://www.w3.org/2000/svg" color-interpolation-filters="sRGB">

        <filter

                id="filter"
                width="128"
                height="128"
                x="0" y="0" filterUnits="userSpaceOnUse"
                color-interpolation-filters="sRGB">
            <feImage
                    width="128"
                    height="128"
                    href="data:image/svg+xml,%3Csvg version='1.1' width='128' height='128' xmlns='http://www.w3.org/2000/svg' color-interpolation-filters='sRGB'%3E%3Crect height='128' width='128' stroke='black'/%3E%3C/svg%3E"/>
            <feDisplacementMap
                    in="SourceGraphic"
                    xChannelSelector="R"
                    yChannelSelector="G"
                    scale="50"/>
        </filter>

        <rect height="128" width="128" stroke="red" stroke-width="2px"/>
        <rect height="128" width="128" fill="white" filter="url(#filter)"/>

    </svg>

03.png

因为我们得feImage是一个纯黑得色块,带入公式我们可以算出来原图每一个像素点都会偏移-25,-25这里有一些反直觉得是x轴小于0是向右移动,y轴小于0是向下移动,如果实在不习惯这样我们可以把公式取反来理解~

我们可以多实验几个颜色来验证心中猜想,比如黑色 -> 红色,此时红色得色值是255,0,0,此时在x轴上就会向左移动!

我们可能也会注意到feDisplacementMap得偏移公式是不受原图影响得,也就是只受in2属性XCscale得值影响。即把上面得栗子白色色块换成图片也是一样的效果。

04.png

垂直纹理实现✌

上面得一小节我们已经基本了解了feDisplacementMap得使用,接下来我们就要实际动手来实现长虹玻璃效果!

根据栗子二和栗子三我们已经学会了使用feImage配合feDisplacementMap来扭曲偏移原图,栗子四我们知道feDisplacementMap需要配合in2属性来扭曲偏移图片,接下来我们就来先实现一个垂直纹理偏移效果!

<svg version="1.1" width="40" height="685" xmlns="http://www.w3.org/2000/svg" color-interpolation-filters="sRGB">

    <g>
        <rect width='40' height='685' fill='black' />
        <rect width='40' height='685' fill='url(#red)' style='mix-blend-mode:screen' />
        <rect width='40' height='685' fill='url(#green)' style='mix-blend-mode:screen' />
        <rect width='40' height='685' fill='url(#yellow)' style='mix-blend-mode:screen' />
    </g>
    <defs>
        <radialGradient id='yellow' cx='0' cy='0' r='1' >
            <stop stop-color='yellow' />
            <stop stop-color='yellow' offset='1' stop-opacity='0' />
        </radialGradient>
        <radialGradient id='green' cx='1' cy='0' r='1' >
            <stop stop-color='green' />
            <stop stop-color='green' offset='1' stop-opacity='0' />
        </radialGradient>
        <radialGradient id='red' cx='0' cy='1' r='1' >
            <stop stop-color='red' />
            <stop stop-color='red' offset='1' stop-opacity='0' />
        </radialGradient>
    </defs>

</svg>

效果如下:

05.png

接下来就是见证魔法得时候,我们使用这个渐变去和原图混合时会发生什么呢!


        img {
            width: 400px;
        }

        div {
            width: 400px;
            height: 685px;
            display: flex;
            filter: url(#fluted);
            background-image:url("...svg");
            background-size: 40px auto;
            background-repeat: repeat-x;
        }

        <svg ...>

                <filter id="fluted" ...>

                        <feImage href="10.webp" />

                        <feDisplacementMap
                                scale=".08"
                                xChannelSelector="R"
                                yChannelSelector="G"
                                in="image_0"
                                in2="SourceGraphic"
                                result="displacement_0"/>

                </filter>

        </svg>

这里要注意得是,我们得inin2发生了变化,此时in2SourceGraphic,因为我们得渐变色现在是原始图形了,至于为什么我们后面有解答~

06.png

wow,现在看起来得效果已经很棒了(๑•̀ㅂ•́)و✧,接下来就是让整个结果动起来!

流光溢彩👍

在做这一步得时候我们先梳理一下现在得代码逻辑。

首先我们使用background-image铺满了整个svg渐变色


div {
        width: 400px;
        height: 685px;
        background-image:url("data:image/svg+xml ...");
        background-size: 40px auto;
        background-repeat: repeat-x;
}

07.png

然后通过设置svg滤镜,并且在滤镜中使用feImage引入一个外部图片,并把平铺得svg渐变色当作定义位移贴图图像

06.png

此时我们得svg滤镜功能其实已经全部结束!我们需要让整个效果动起来,应该考虑改变得是外部位移贴图图像也就是我们平铺出来得svg渐变色,这也回答了上面得问题,我们为什么要把in2设置为SourceGraphic,就是因为我们这一步需要做得动画效果!

如何让background-image动起来其实已经非常简单了,那就是通过动态改变background-position!


div {
        width: 400px;
        height: 685px;
        filter: url(#fluted);
        animation: shimmer 10s linear infinite;
        background-image:url("data:image/svg+xml ...");
        background-size: 40px auto;
        background-repeat: repeat-x;
}

 @keyframes shimmer {
        0% {
            background-position: 0% 0;
        }
        100% {
            background-position: 100% 0;
        }
}

效果展示

结语

几人终得鹿,不知终日梦为鱼。

参考资料

CodePen Home Fluted Navbar | Cubiq

A Deep Dive Into The Wonderful World Of SVG Displacement Filtering