CSS 如何實現羽化效果?

XboxYan 發表於 2022-12-15
CSS
歡迎關注我的公眾號:前端偵探

最近碰到這樣一個問題,在一張封面上直接顯示書名,可能會存在書名看不太清楚的情況(容易受到背景干擾),如下

image-20221210163633853

為了解決這個問題,設計師提了一個“究極”方案,將書名背後的圖片模糊一下,這個在 CSS 中很好實現,僅需backdrop-filter即可

.name{
  backdrop-filter: blur(10px);
}

當然,現在模糊是模糊了,但是邊緣過於“斷層”,導致書名和封面有些“格格不入”,效果如下

image-20221210163925282

如果能夠將邊緣羽化一下,虛化邊緣效果,就可以很好地將書名融入到背景當中

羽化是photoshop術語,羽化原理是令選區內外銜接部分虛化,起到漸變的作用從而達到自然銜接的效果,是ps及其其它版本中的處理圖片的重要工具

這是設計最終羽化後的效果,既能保證文字清晰可見,又能避免太過突兀,如下

image-20221210165427443

設計師實現這個很簡單,用橡皮擦擦一下就可以了,那麼 CSS 呢?下面來探討一下 CSS 如何實現羽化的效果。

一、羽化的原理

羽化其實就是將邊緣變得模糊,在 CSS 中其實就是建立一個邊緣模糊的遮罩(mask),也就是需要一種半透明的漸變。

關於遮罩,這裡簡單介紹一下,基本語法很簡單,和background的語法基本一致

.content{
  -webkit-mask: '遮罩圖片' ;
}
/*完整語法*/
.content{
  -webkit-mask: '遮罩圖片' [position] / [size] ;
}

這裡的遮罩圖片和背景的使用方式基本一致,可以是PNG圖片SVG圖片、也可以是漸變繪製的圖片,同時也支援多圖片疊加

遮罩的原理很簡單,最終效果只顯示不透明的部分,透明部分將不可見,半透明類推

img

先舉個圓形的例子,這個比較簡單,因為可以直接透過徑向漸變建立

比如,下面有一個頭像,現在直接放在背景上非常突兀

image-20221210172243765

我們可以將這個頭像透過徑向漸變繪製出了一個從不透明到透明的遮罩,達到和背景融合的效果

.head{
  -webkit-mask: radial-gradient(closest-side circle,#000 60%, transparent 100%);
}

原理是這樣的

image-20221210173118913

最後效果如下

image-20221210172534020

當然這是圓形的,如果是矩形的呢?

二、矩形的羽化原理

根據上面的分析,如果希望羽化矩形邊緣,需要建立這一個遮罩

image-20221210174740599

那麼問題來了,如何建立這一個邊緣模糊的矩形呢?貌似沒辦法直接透過漸變來實現,而且還需要是尺寸自適應的(自動跟隨容器尺寸)

如果單純看這樣一個矩形,還是很容易實現的,透過box-shadow即可實現

.shadow{
  width: 200px;
  height: 200px;
     background:black;
  border-radius:10px;
  box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black
}

根據需求,可以多疊加幾層box-shadow,這裡疊加了3層,效果如下

image-20221210175454541

但是問題又來了,這樣一個 dom 無法直接用作mask-image,那如何處理呢?

三、透過 SVG foreignObject 轉換成圖片

上面提到,透過 CSS 陰影可以很輕鬆的實現我們說需要的效果,但可惜現在還是 dom 階段,所以需要將這個 dom 轉換成影像。

在這裡,需要藉助 SVG 中的foreignObject元素,透過這個元素,可以將 HTML嵌入到SVG中,輕鬆實現 dom 轉圖片的效果

有興趣的可以參考之前這幾篇文章

原理如下img

回到這裡,我們僅需要將上面的結果放到foreignObject元素中,由於需要自適應尺寸,這裡的body寬高都是100%,如下

<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject width="100%" height="100%">
    <body class="wrap" xmlns="http://www.w3.org/1999/xhtml">
      <style>
        .wrap{
          box-sizing: border-box;
          margin: 0;
          height: 100%;
          padding: 10px;
        }
        .shadow{
          height: 100%;
          background:black;
          border-radius:10px;
          box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black
        }
      </style>
      <div class="shadow"></div>
    </body>
  </foreignObject>
</svg>

這樣就得到了一個寬高自適應的SVG影像,無論寬高怎麼變化,都是撐滿的

image-20221210180715580

別看這麼多標籤,這其實已經是一張圖片,可以直接使用,接下來看看如何運用

四、矩形的羽化

其實上面得到的 SVG可以直接當成一個圖片資源,正常使用了,就像這樣

.name{
  -webkit-mask: url('./fearher.svg')
}

不過,也可以將這個SVG圖片轉換成內聯形式,減少資源依賴,轉換後仍然保持自適應特性

這裡推薦張鑫旭老師的SVG線上壓縮合並工具

轉換後就是這個樣子

.name{
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3CforeignObject width='100%25' height='100%25'%3E%3Cbody class='wrap' xmlns='http://www.w3.org/1999/xhtml'%3E%3Cstyle%3E.wrap%7Bbox-sizing:border-box;margin:0;height:100%25;padding:10px%7D.shadow%7Bheight:100%25;background:%23000;border-radius:10px;box-shadow:0 0 5px %23000,0 0 10px %23000,0 0 15px %23000%7D%3C/style%3E%3Cdiv class='shadow'/%3E%3C/body%3E%3C/foreignObject%3E%3C/svg%3E")
}

效果如下

Kapture 2022-12-10 at 18.23.20

而且由於尺寸是動態的,換個書名也能完美適應

image-20221210221842455

最後再來對比一下,下面哪個一眼看上去效果最好

image-20221210183113086

完整程式碼可以檢視以下任意連結

五、總結一下

以上就是本文全部內容了,一個還不錯的繪製小技巧,最後來回顧一下一些實現要點

  1. 羽化其實就是將邊緣變得模糊,在 CSS 中其實就是建立一個邊緣模糊的遮罩,也就是需要一種半透明的漸變。
  2. 圓形的邊緣很好羽化,因為徑向漸變可以直接繪製
  3. 矩形的邊緣就稍微複雜點,因為不能直接透過漸變繪製
  4. 邊緣模糊在 CSS 中很好實現,用 box-shadow 就行了
  5. 可以透過 SVG foreignObject 將 dom 轉換成圖片
  6. SVG 轉換成內聯形式,好處是減少資源依賴,轉換後仍然保持自適應特性

當然本文最重要的一點是,如何在 HTML 嚴重受限的背景上繪製一些常見的圖形,以後碰到類似的需求也可以朝這個方向去考慮,最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探