巧用 SVG 濾鏡還能製作表情包?

本文將介紹一些使用 SVG feTurbulence 濾鏡實現的一些有趣、大膽的的動效。

系列另外兩篇:

背景

今天在群裡面聊天,看到有人發這個表情包:

剛好最近一直在學習 SVG,腦海中就把這個表情包的效果和 feTurbulence 濾鏡關聯了起來。

如果我們有一張類似上圖表情包的靜態圖,利用 feTurbulence 生成的雜訊函數,運用在靜態的表情包之上,再添加些許動畫,是不是也能製作一張類似的動圖效果呢?

什麼是 SVG feTurbulence 濾鏡?

如果你對 SVG 濾鏡還不算太了解,可以簡單看看我的這篇文章入門:有意思!強大的 SVG 濾鏡

這裡我們會用到 SVG 中的 feTurbulence 濾鏡。再簡單介紹下。

feTurbulence 濾鏡

turbulence 意為湍流,不穩定氣流,而 SVG <feTurbulence> 濾鏡能夠實現半透明的煙熏或波狀影像。通常用於實現一些特殊的紋理。濾鏡利用 Perlin 雜訊函數創建了一個影像。雜訊在模擬雲霧效果時非常有用,能產生非常複雜的質感,利用它可以實現了人造紋理比如說雲紋、大理石紋的合成。

簡單看個 DEMO:

<div>Coco</div>
<div class="turbulence">Coco</div>

<svg>
    <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
        <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.03" numOctaves="1" />
        <feDisplacementMap in="SourceGraphic" scale="50"></feDisplacementMap>
    </filter>
</svg>
.turbulence {
    filter: url(#fractal);
}

左邊是正常的效果,後邊是應用了 <feTurbulence> 的效果,你可以試著點進 Demo,更改 baseFrequencynumOctaves 參數的大小,可以看到不同的效果:

CodePen Demo — feTurbulence text demo

feTurbulence 濾鏡應用於圖片

我們嘗試把上述 DEMO 中的文字轉換成圖片。我找到了一張靜態的哭的表情包:

簡單改造下程式碼:

<div></div>
<svg>
    <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
        <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.09" numOctaves="1" ></feTurbulence>
        <feDisplacementMap in="SourceGraphic" scale="15"></feDisplacementMap>
    </filter>
</svg>
div {
    background: url(image.jpg);
    filter: url(#fractal);
}

效果如下:

image

有點那個意思了,我們通過 feTurbulence 濾鏡得到了雜訊圖形,然後通過 feDisplacementMap 濾鏡根據 feTurbulence 所產生的雜訊圖形進行形變,扭曲,液化,得到最終的效果。

通過調整 feTurbulence 中的 baseFrequencynumOctaves 以及 feDisplacementMap 中的 scale 參數,我們可以調試得到不同的效果。

接下來,我們再給上述濾鏡添加一個動畫,利用 SVG 的 animate 標籤,動態的改變 baseFrequency 參數:

<svg>
    <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
        <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.1 0.1" numOctaves="1" >
            <animate
                    attributeName="baseFrequency"
                    from="0.1 0.1"
                    to="0.08 0.01"
                    dur="3.5s"
                    repeatCount="indefinite"/>
        </feTurbulence>
        <feDisplacementMap in="SourceGraphic" scale="15"></feDisplacementMap>
    </filter>
</svg>

添加了動畫之後,同樣作用於圖片之上,我們就可以得到如下的效果:

由於截圖軟體的幀率問題,看著有點慢,你可以戳進 DEMO 看看實際效果,還是挺有意思的,至此我們就簡單的利用 CSS 配合 SVG 的方式,通過一張靜態圖得到了一個動態的表情包啦。😁

CodePen Demo — 使用 SVG 濾鏡 feTurbulence 讓圖片動起來

巧用 feTurbulence 濾鏡實現各種動效

嘿,feTurbulence 當然不是僅能實現這個而已,下面我們再探索一些有意思的場景。

首先,再明確下我們主要使用到的兩個濾鏡 feTurbulencefeDisplacementMap,它們的核心程式碼:

<svg>
    <filter id="feDisplacementMap" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="64">
        <feTurbulence type="fractalNoise" baseFrequency="0.0995" numOctaves="1" result="img" />
        <feDisplacementMap id="feDis" in="SourceGraphic" in2="img" scale="600" />
    </filter>
</svg>

其中濾鏡中的幾個參數 — baseFrequencynumOctavesscale 的改變其實都會得到不一樣的效果。我們動態的變化其中的一個或多個也都可以得到不同的動畫效果。

動態改變 feDisplacementMapscale 的參數

feDisplacementMap 濾鏡是用於改變元素和圖形的像素位置的。該濾鏡通過遍歷原圖形的所有像素點,通過 feTurbulence 濾鏡產生的雜訊函數將原影像的每個像素點重新映射到一個新的位置,形成一個新的圖形。

scale 表示新得到的影像的扭曲程度,這個值越大,影像越加扭曲不可識別。

通過設置一個非常大初始值,我們可以完全將輸入的任何源影像粒子化,看看這個 Demo:

<div></div>
<div class="fractal"></div>

<svg viewBox="0 0 200 200" width="200px" height="200px">
    <defs>
        <filter id="fractal" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
            <feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="10" seed="1" stitchTiles="noStitch" result="img" />
            <feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="600" />
        </filter>
    </defs>
</svg>
div {
    width: 200px;
    height: 200px;
    background: url(image.jpeg)
}

.fractal {
    filter: url(#fractal);
}

左邊為正常的影像,右邊為作用了設置了 SVG 濾鏡效果的影像,並且設置了 scale="600",完全將圖片粒子化了:

這個時候,讓濾鏡的 scale="600" 動態變化回 scale="1"(當此參數為 1 時,影像表示為正常狀態),也就能實現一個圖形從粒子化到正常化的轉變:

<svg viewBox="0 0 200 200" width="200px" height="200px">
    <filter id="fractal" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
        <feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="10" seed="1" result="img" />
        <feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="600">
            <animate attributeName="scale" values="600;0;0" keyTimes="0;0.75;1" begin="0s" dur="2s" repeatCount="indefinite" />
        </feDisplacementMap>
    </filter>
</svg>

效果如下:

CodePen — SVG Filter feTurbulence & feDisplacementMap 實現圖片粒子化復原動畫

動態改變 feDisplacementMapscale 的參數實現一些開獎動效

基於上述的效果,我們可以實現這樣一類效果,譬如一些開獎結果,一開始它是模糊的,但是用戶點擊之後,模糊的結果逐漸從模糊到真實。

但是點擊事件,由於 SVG Animate 標籤的一些限制,需要藉助一些 Javascript 程式碼,這裡借用 JQuery 簡單做個示意。

我們有一串開獎數組 745846,實現點擊之後從模糊到真實:

<div id="fe1" class="fe1">745846</div>
<svg>
    <filter id="feDisplacementMap" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="64">
        <feTurbulence type="fractalNoise" baseFrequency="0.0995" numOctaves="1" result="img" />
        <feDisplacementMap id="feDis" in="SourceGraphic" in2="img" scale="200" />
    </filter>
</svg>
$("#fe1").click((e) => {
    const filter = $("#feDis");
    const startTime = Date.now();
    const duration = 1000;
    const target = 200;
    
    requestAnimationFrame(function aniMove() {
        const t = Math.min(1, (Date.now() - startTime) / duration);
        const nextTarget = target - (t * target) + 1;
        
        filter.attr('scale', nextTarget);

        if (t < 1.0) {
            requestAnimationFrame(aniMove);
        }
    });
});

點擊之前的狀態如下:

點擊之後:

上述效果,你可以套用到任何地方,完整的 Demo 地址:

CodePen Demo — SVG Filter Button Effects

動態改變 feDisplacementMapscale 的參數實現一些 fadeOut 動畫

當然,上述的效果也是可以反著來的,就是一張圖(或者任何元素),點擊之後粒子化,然後漸變的消失,進階版的 fadeOut 效果。

通過動態的改變濾鏡的參數和圖片的透明度,當然,也需要藉助一些 JavaScript 程式碼,完整的程式碼就不貼了(與上述 DEMO 非常類似),直接上效果圖:

使用 SVG 濾鏡實現任意元素粒子化 FadeOut 效果.gif

是不是非常類似滅霸把人物消失的效果?之前看過這樣一篇文章 – Google滅霸彩蛋的效果實現,其中介紹了一種使用 Canvas 實現類似效果的方式,本文這裡使用 SVG 濾鏡達成了近似的效果。

對源碼感興趣的可以猛戳下面的 Demo,效果也是可以方便的移植到其他元素之上:

CodePen Demo — 使用 SVG 濾鏡實現任意元素粒子化 FadeOut 效果

不要吹滅你的靈感和你的想像力; 不要成為你的模型的奴隸。 ——文森特・梵高

最後

好了,本文到此結束,希望對你有幫助 😃

更多精彩 CSS 技術文章匯總在我的 Github — iCSS ,持續更新,歡迎點個 star 訂閱收藏。

想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 — iCSS前端趣聞 😄

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。